mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
添加鼠标单击烟花特效
This commit is contained in:
@@ -49,6 +49,9 @@ const BLOG = {
|
||||
CONTACT_GITHUB: 'https://github.com/tangly1024',
|
||||
CONTACT_TELEGRAM: '',
|
||||
|
||||
// 鼠标点击烟花特效
|
||||
FIREWORKS: process.env.NEXT_PUBLIC_FIREWORKS || true, // 鼠标点击烟花特效
|
||||
|
||||
// 悬浮挂件
|
||||
WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件
|
||||
WIDGET_PET_LINK:
|
||||
|
||||
209
components/Fireworks.js
Normal file
209
components/Fireworks.js
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* https://codepen.io/juliangarnier/pen/gmOwJX
|
||||
* custom by hexo-theme-yun @YunYouJun
|
||||
*/
|
||||
import React from 'react'
|
||||
import anime from 'animejs'
|
||||
|
||||
export const Fireworks = () => {
|
||||
React.useEffect(() => {
|
||||
createFireworks({})
|
||||
}, [])
|
||||
return <canvas id='fireworks' className='fireworks'></canvas>
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建烟花
|
||||
* @param config
|
||||
*/
|
||||
function createFireworks(config) {
|
||||
const defaultColors = ['102, 167, 221', '62, 131, 225', '33, 78, 194']
|
||||
const defaultConfig = {
|
||||
colors: defaultColors,
|
||||
numberOfParticules: 20,
|
||||
orbitRadius: {
|
||||
min: 50,
|
||||
max: 100
|
||||
},
|
||||
circleRadius: {
|
||||
min: 10,
|
||||
max: 20
|
||||
},
|
||||
diffuseRadius: {
|
||||
min: 50,
|
||||
max: 100
|
||||
},
|
||||
animeDuration: {
|
||||
min: 900,
|
||||
max: 1500
|
||||
}
|
||||
}
|
||||
config = Object.assign(defaultConfig, config)
|
||||
|
||||
let pointerX = 0
|
||||
let pointerY = 0
|
||||
|
||||
// sky blue
|
||||
const colors = config.colors || defaultColors
|
||||
|
||||
const canvasEl = document.querySelector('.fireworks')
|
||||
const ctx = canvasEl.getContext('2d')
|
||||
|
||||
/**
|
||||
* 设置画布尺寸
|
||||
*/
|
||||
function setCanvasSize(canvasEl) {
|
||||
canvasEl.width = window.innerWidth
|
||||
canvasEl.height = window.innerHeight
|
||||
canvasEl.style.width = `${window.innerWidth}px`
|
||||
canvasEl.style.height = `${window.innerHeight}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* update pointer
|
||||
* @param {TouchEvent} e
|
||||
*/
|
||||
function updateCoords(e) {
|
||||
pointerX =
|
||||
e.clientX ||
|
||||
(e.touches[0] ? e.touches[0].clientX : e.changedTouches[0].clientX)
|
||||
pointerY =
|
||||
e.clientY ||
|
||||
(e.touches[0] ? e.touches[0].clientY : e.changedTouches[0].clientY)
|
||||
}
|
||||
|
||||
function setParticuleDirection(p) {
|
||||
const angle = (anime.random(0, 360) * Math.PI) / 180
|
||||
const value = anime.random(
|
||||
config.diffuseRadius.min,
|
||||
config.diffuseRadius.max
|
||||
)
|
||||
const radius = [-1, 1][anime.random(0, 1)] * value
|
||||
return {
|
||||
x: p.x + radius * Math.cos(angle),
|
||||
y: p.y + radius * Math.sin(angle)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定位置创建粒子
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns
|
||||
*/
|
||||
function createParticule(x, y) {
|
||||
const p = {
|
||||
x,
|
||||
y,
|
||||
color: `rgba(${
|
||||
colors[anime.random(0, colors.length - 1)]
|
||||
},${
|
||||
anime.random(0.2, 0.8)
|
||||
})`,
|
||||
radius: anime.random(config.circleRadius.min, config.circleRadius.max),
|
||||
endPos: null,
|
||||
draw() {}
|
||||
}
|
||||
p.endPos = setParticuleDirection(p)
|
||||
p.draw = function() {
|
||||
ctx.beginPath()
|
||||
ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true)
|
||||
ctx.fillStyle = p.color
|
||||
ctx.fill()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
function createCircle(x, y) {
|
||||
const p = {
|
||||
x,
|
||||
y,
|
||||
color: '#000',
|
||||
radius: 0.1,
|
||||
alpha: 0.5,
|
||||
lineWidth: 6,
|
||||
draw() {}
|
||||
}
|
||||
p.draw = function() {
|
||||
ctx.globalAlpha = p.alpha
|
||||
ctx.beginPath()
|
||||
ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true)
|
||||
ctx.lineWidth = p.lineWidth
|
||||
ctx.strokeStyle = p.color
|
||||
ctx.stroke()
|
||||
ctx.globalAlpha = 1
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
function renderParticule(anim) {
|
||||
for (let i = 0; i < anim.animatables.length; i++) { anim.animatables[i].target.draw() }
|
||||
}
|
||||
|
||||
function animateParticules(x, y) {
|
||||
const circle = createCircle(x, y)
|
||||
const particules = []
|
||||
for (let i = 0; i < config.numberOfParticules; i++) { particules.push(createParticule(x, y)) }
|
||||
|
||||
anime
|
||||
.timeline()
|
||||
.add({
|
||||
targets: particules,
|
||||
x(p) {
|
||||
return p.endPos.x
|
||||
},
|
||||
y(p) {
|
||||
return p.endPos.y
|
||||
},
|
||||
radius: 0.1,
|
||||
duration: anime.random(
|
||||
config.animeDuration.min,
|
||||
config.animeDuration.max
|
||||
),
|
||||
easing: 'easeOutExpo',
|
||||
update: renderParticule
|
||||
})
|
||||
.add(
|
||||
{
|
||||
targets: circle,
|
||||
radius: anime.random(config.orbitRadius.min, config.orbitRadius.max),
|
||||
lineWidth: 0,
|
||||
alpha: {
|
||||
value: 0,
|
||||
easing: 'linear',
|
||||
duration: anime.random(600, 800)
|
||||
},
|
||||
duration: anime.random(1200, 1800),
|
||||
easing: 'easeOutExpo',
|
||||
update: renderParticule
|
||||
},
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
const render = anime({
|
||||
duration: Infinity,
|
||||
update: () => {
|
||||
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height)
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener(
|
||||
'mousedown',
|
||||
(e) => {
|
||||
render.play()
|
||||
updateCoords(e)
|
||||
animateParticules(pointerX, pointerY)
|
||||
},
|
||||
false
|
||||
)
|
||||
|
||||
setCanvasSize(canvasEl)
|
||||
window.addEventListener(
|
||||
'resize',
|
||||
() => {
|
||||
setCanvasSize(canvasEl)
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
"@next/bundle-analyzer": "^12.1.1",
|
||||
"@popperjs/core": "^2.9.3",
|
||||
"animate.css": "^4.1.1",
|
||||
"animejs": "^3.2.1",
|
||||
"axios": ">=0.21.1",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"feed": "^4.2.2",
|
||||
|
||||
@@ -15,6 +15,7 @@ import dynamic from 'next/dynamic'
|
||||
import { GlobalContextProvider } from '@/lib/global'
|
||||
import { DebugPanel } from '@/components/DebugPanel'
|
||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||
import { Fireworks } from '@/components/Fireworks'
|
||||
|
||||
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
||||
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
|
||||
@@ -36,13 +37,14 @@ const MyApp = ({ Component, pageProps }) => {
|
||||
{JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <Busuanzi />}
|
||||
{BLOG.ADSENSE_GOOGLE_ID && <GoogleAdsense />}
|
||||
{BLOG.FACEBOOK_APP_ID && BLOG.FACEBOOK_PAGE_ID && <Messenger />}
|
||||
{JSON.parse(BLOG.FIREWORKS) && <Fireworks/>}
|
||||
</>
|
||||
|
||||
return (
|
||||
<GlobalContextProvider>
|
||||
{externalPlugins}
|
||||
{/* FontawesomeCDN */}
|
||||
<link rel="stylesheet" href={BLOG.FONT_AWESOME_PATH} referrerPolicy="no-referrer" />
|
||||
{externalPlugins}
|
||||
<Component {...pageProps} />
|
||||
</GlobalContextProvider>
|
||||
)
|
||||
|
||||
@@ -145,4 +145,11 @@ nav {
|
||||
|
||||
.notion-code-copy-button > svg{
|
||||
pointer-events:none
|
||||
}
|
||||
}
|
||||
|
||||
.fireworks{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
pointer-events: none;}
|
||||
Reference in New Issue
Block a user