<!DOCtype html> | |
<html> | |
<style> | |
* { | |
box-sizing: border-box; | |
} | |
html, | |
body { | |
height: 100%; | |
width: 100%; | |
} | |
body { | |
margin: 0; | |
overflow: hidden; | |
} | |
</style> | |
<body> | |
<script> | |
"use strict"; | |
function getFullScreenParams(ctx) { | |
const { width, height } = ctx.canvas; | |
return [0, 0, width, height]; | |
} | |
function getCtxUtils(ctx) { | |
function clear() { | |
const params = getFullScreenParams(ctx); | |
ctx.clearRect(...params); | |
} | |
function save(callback) { | |
ctx.save(); | |
callback(); | |
ctx.restore(); | |
} | |
function drawPath(callback) { | |
ctx.beginPath(); | |
callback(); | |
ctx.closePath(); | |
} | |
return { | |
clear, | |
save, | |
drawPath | |
}; | |
} | |
function initCanvas(resizeCallback, parent = document.body) { | |
function create() { | |
const canvas = document.createElement('canvas'); | |
const ctx = canvas.getContext('2d'); | |
return [canvas, ctx]; | |
} | |
function resize() { | |
const { clientHeight, clientWidth } = parent; | |
canvas.height = clientHeight; | |
canvas.width = clientWidth; | |
resizeCallback(); | |
} | |
function append() { | |
parent.innerHTML = ''; | |
parent.appendChild(canvas); | |
} | |
const [canvas, ctx] = create(); | |
resize(); | |
append(); | |
window.addEventListener('resize', resize); | |
return ctx; | |
} | |
class Curve { | |
constructor(origin, angle, length = 400) { | |
this.angle = angle; | |
this.length = length; | |
this._angleOffset = 40; | |
this.percentage = 0; | |
this.speed = (Math.random() * 0.005) + 0.0005; | |
this.randColor = () => { | |
const { floor, random } = Math; | |
return ['cyan', 'magenta', 'yellow'][floor(random() * 3)]; | |
}; | |
this.getEndPoint = () => { | |
const { cos, sin } = Math; | |
const x = cos(this.angle) * this.length; | |
const y = sin(this.angle) * this.length; | |
return [x, y]; | |
}; | |
this.getStartControlPoint = () => { | |
const { cos, sin } = Math; | |
const angle = this.angle - (this._angleOffset / 60); | |
const length = this.length / 3; | |
const x = cos(angle) * length; | |
const y = sin(angle) * length; | |
return [x, y]; | |
}; | |
this.getEndControlPoint = () => { | |
const { cos, sin } = Math; | |
const angle = this.angle + (this._angleOffset / 60); | |
const length = (this.length / 3) * 2; | |
const x = cos(angle) * length; | |
const y = sin(angle) * length; | |
return [x, y]; | |
}; | |
this.animate = () => { | |
function getXY() { | |
const x = cubicN(percentage, 0, startControlPoint[0], endControlPoint[0], end[0]); | |
const y = cubicN(percentage, 0, startControlPoint[1], endControlPoint[1], end[1]); | |
return [x, y]; | |
} | |
// cubic helper formula | |
function cubicN(T, a, b, c, d) { | |
const t2 = T * T; | |
const t3 = t2 * T; | |
return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3; | |
} | |
const { startControlPoint, endControlPoint, end, percentage } = this; | |
if (!this.isComplete) { | |
this.percentage += this.speed; | |
this.currentPoint = getXY(); | |
} | |
}; | |
this.start = origin; | |
this.currentPoint = [0, 0]; | |
this.end = this.getEndPoint(); | |
this.startControlPoint = this.getStartControlPoint(); | |
this.endControlPoint = this.getEndControlPoint(); | |
this.color = this.randColor(); | |
} | |
get isComplete() { | |
return this.percentage >= 1; | |
} | |
} | |
class Burst { | |
constructor(x, y) { | |
this.curves = []; | |
this.render = (ctx) => { | |
function renderCurve(curve) { | |
const { currentPoint, endControlPoint, startControlPoint, end } = curve; | |
save(() => { | |
drawPath(() => { | |
ctx.moveTo(0, 0); | |
ctx.strokeStyle = 'rgba(80,80,80,0.4)'; | |
ctx.bezierCurveTo(...startControlPoint, ...endControlPoint, ...end); | |
ctx.stroke(); | |
}); | |
}); | |
save(() => { | |
ctx.translate(x, y); | |
drawPath(() => { | |
let [xi, yi] = currentPoint; | |
xi -= x; | |
yi -= y; | |
ctx.fillStyle = curve.color; | |
ctx.moveTo(0, 0); | |
ctx.arc(xi, yi, 4, 0, Math.PI * 2); | |
ctx.fill(); | |
}); | |
}); | |
} | |
function renderCurves() { | |
save(() => { | |
ctx.translate(x, y); | |
curves.forEach(renderCurve); | |
}); | |
} | |
function renderBackground() { | |
save(() => { | |
ctx.translate(0, 0); | |
ctx.fillStyle = '#101219'; | |
drawPath(() => ctx.fillRect(...getFullScreenParams(ctx))); | |
}); | |
} | |
const { clear, save, drawPath } = getCtxUtils(ctx); | |
const { curves, x, y } = this; | |
clear(); | |
renderBackground(); | |
renderCurves(); | |
}; | |
this._x = x; | |
this._y = y; | |
this.create(); | |
} | |
get x() { return this._x; } | |
set x(value) { | |
this._x = value; | |
if (this.onPositionChange) | |
this.onPositionChange(); | |
} | |
get y() { return this._y; } | |
set y(value) { | |
this._y = value; | |
if (this.onPositionChange) | |
this.onPositionChange(); | |
} | |
create() { | |
const chunkSize = 6 / 32; | |
for (let i = 0; i <= 6; i += chunkSize) { | |
this.curves.push(new Curve([this.x, this.y], i, 1300)); | |
} | |
} | |
} | |
window.onload = init; | |
function init() { | |
function animate() { | |
burst.curves.forEach((curve, i) => { | |
if (curve.isComplete) { | |
const chunkSize = 6 / 32; | |
const newCurve = new Curve([burst.x, burst.y], i * chunkSize, 1300); | |
burst.curves.splice(i, 1, newCurve); | |
} | |
else { | |
curve.animate(); | |
} | |
}); | |
burst.render(ctx); | |
requestAnimationFrame(animate); | |
} | |
let [cx, cy] = [window.innerWidth / 2, window.innerHeight / 2]; | |
const burst = new Burst(cx, cy); | |
const ctx = initCanvas(() => { | |
[cx, cy] = [window.innerWidth / 2, window.innerHeight / 2]; | |
burst.x = cx; | |
burst.y = cy; | |
}); | |
burst.render(ctx); | |
requestAnimationFrame(animate); | |
} | |
</script> | |
</body> | |
</html> |
Move along Curve html css java Code
Friday, August 04, 2023
0
Tags
Please don't spam comments Thank You.