123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- // 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() {
- 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()
- }
|