halo-theme-dream
Version:
梦之城,童话梦境,动漫类型博客主题。
243 lines (204 loc) • 6.3 kB
JavaScript
// The springy emoji effect has been translated over from this old
// code, to modern js & canvas
// - http://www.yaldex.com/FSMessages/ElasticBullets.htm
function springyEmojiCursor(options) {
let emoji = (options && options.emoji) || '🤪'
let hasWrapperEl = options && options.element
let element = hasWrapperEl || document.body
let nDots = 7
let DELTAT = 0.01
let SEGLEN = 10
let SPRINGK = 10
let MASS = 1
let GRAVITY = 50
let RESISTANCE = 10
let STOPVEL = 0.1
let STOPACC = 0.1
let DOTSIZE = 11
let BOUNCE = 0.7
let width = window.innerWidth
let height = window.innerHeight
let cursor = { x: width / 2, y: width / 2 }
let particles = []
let canvas, context
let emojiAsImage
function init() {
canvas = document.createElement('canvas')
context = canvas.getContext('2d')
canvas.style.top = '0px'
canvas.style.left = '0px'
canvas.style.pointerEvents = 'none'
if (hasWrapperEl) {
canvas.style.position = 'absolute'
element.appendChild(canvas)
canvas.width = element.clientWidth
canvas.height = element.clientHeight
} else {
canvas.style.position = 'fixed'
document.body.appendChild(canvas)
canvas.width = width
canvas.height = height
}
// Save emoji as an image for performance
context.font = '16px serif'
context.textBaseline = 'middle'
context.textAlign = 'center'
let measurements = context.measureText(emoji)
let bgCanvas = document.createElement('canvas')
let bgContext = bgCanvas.getContext('2d')
bgCanvas.width = measurements.width
bgCanvas.height = measurements.actualBoundingBoxAscent * 2
bgContext.textAlign = 'center'
bgContext.font = '16px serif'
bgContext.textBaseline = 'middle'
bgContext.fillText(
emoji,
bgCanvas.width / 2,
measurements.actualBoundingBoxAscent
)
emojiAsImage = bgCanvas
let i = 0
for (i = 0; i < nDots; i++) {
particles[i] = new Particle(emojiAsImage)
}
bindEvents()
loop()
}
// Bind events that are needed
function bindEvents() {
element.addEventListener('mousemove', onMouseMove)
element.addEventListener('touchmove', onTouchMove, { passive: true })
element.addEventListener('touchstart', onTouchMove, { passive: true })
window.addEventListener('resize', onWindowResize)
}
function onWindowResize(e) {
width = window.innerWidth
height = window.innerHeight
if (hasWrapperEl) {
canvas.width = element.clientWidth
canvas.height = element.clientHeight
} else {
canvas.width = width
canvas.height = height
}
}
function onTouchMove(e) {
if (e.touches.length > 0) {
if (hasWrapperEl) {
const boundingRect = element.getBoundingClientRect()
cursor.x = e.touches[0].clientX - boundingRect.left
cursor.y = e.touches[0].clientY - boundingRect.top
} else {
cursor.x = e.touches[0].clientX
cursor.y = e.touches[0].clientY
}
}
}
function onMouseMove(e) {
if (hasWrapperEl) {
const boundingRect = element.getBoundingClientRect()
cursor.x = e.clientX - boundingRect.left
cursor.y = e.clientY - boundingRect.top
} else {
cursor.x = e.clientX
cursor.y = e.clientY
}
}
function updateParticles() {
// eslint-disable-next-line no-self-assign
canvas.width = canvas.width
// follow mouse
particles[0].position.x = cursor.x
particles[0].position.y = cursor.y
// Start from 2nd dot
for (i = 1; i < nDots; i++) {
let spring = new vec(0, 0)
if (i > 0) {
springForce(i - 1, i, spring)
}
if (i < nDots - 1) {
springForce(i + 1, i, spring)
}
let resist = new vec(
-particles[i].velocity.x * RESISTANCE,
-particles[i].velocity.y * RESISTANCE
)
let accel = new vec(
(spring.X + resist.X) / MASS,
(spring.Y + resist.Y) / MASS + GRAVITY
)
particles[i].velocity.x += DELTAT * accel.X
particles[i].velocity.y += DELTAT * accel.Y
if (
Math.abs(particles[i].velocity.x) < STOPVEL &&
Math.abs(particles[i].velocity.y) < STOPVEL &&
Math.abs(accel.X) < STOPACC &&
Math.abs(accel.Y) < STOPACC
) {
particles[i].velocity.x = 0
particles[i].velocity.y = 0
}
particles[i].position.x += particles[i].velocity.x
particles[i].position.y += particles[i].velocity.y
let height, width
height = canvas.clientHeight
width = canvas.clientWidth
if (particles[i].position.y >= height - DOTSIZE - 1) {
if (particles[i].velocity.y > 0) {
particles[i].velocity.y = BOUNCE * -particles[i].velocity.y
}
particles[i].position.y = height - DOTSIZE - 1
}
if (particles[i].position.x >= width - DOTSIZE) {
if (particles[i].velocity.x > 0) {
particles[i].velocity.x = BOUNCE * -particles[i].velocity.x
}
particles[i].position.x = width - DOTSIZE - 1
}
if (particles[i].position.x < 0) {
if (particles[i].velocity.x < 0) {
particles[i].velocity.x = BOUNCE * -particles[i].velocity.x
}
particles[i].position.x = 0
}
particles[i].draw(context)
}
}
function loop() {
updateParticles()
requestAnimationFrame(loop)
}
function vec(X, Y) {
this.X = X
this.Y = Y
}
function springForce(i, j, spring) {
let dx = particles[i].position.x - particles[j].position.x
let dy = particles[i].position.y - particles[j].position.y
let len = Math.sqrt(dx * dx + dy * dy)
if (len > SEGLEN) {
let springF = SPRINGK * (len - SEGLEN)
spring.X += (dx / len) * springF
spring.Y += (dy / len) * springF
}
}
function Particle(canvasItem) {
this.position = { x: cursor.x, y: cursor.y }
this.velocity = {
x: 0,
y: 0,
}
this.canv = canvasItem
this.draw = function(context) {
context.drawImage(
this.canv,
this.position.x - this.canv.width / 2,
this.position.y - this.canv.height / 2,
this.canv.width,
this.canv.height
)
}
}
init()
}
new springyEmojiCursor()