Created
March 28, 2025 23:21
-
-
Save alirezarezamand/5c74b692b4e47018098b36258629b006 to your computer and use it in GitHub Desktop.
Canvas Cube
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="container"></div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var mContainer = document.getElementById('container'); | |
var mXPlanes = [], | |
mYPlanes = [], | |
mZPlanes = [], | |
mAllPlanes = []; | |
var mTrails = []; | |
var mPlaneSize = 300, | |
mDivisions = 5, | |
mSegmentSize = mPlaneSize / mDivisions; | |
window.onload = function() { | |
init(); | |
requestAnimationFrame(loop); | |
}; | |
function init() { | |
TweenMax.set(mContainer, { | |
perspective:4000, | |
transformStyle:'preserve-3d' | |
}); | |
createPlanes(); | |
createTrails(); | |
TweenMax.to(mContainer, 20, {ease:Linear.easeNone, rotationX:360, rotationY:360, repeat:-1}) | |
} | |
function createPlanes() { | |
var i, | |
startOffset = mPlaneSize * -0.5; | |
// x | |
for (i = 0; i <= mDivisions; i++) { | |
mXPlanes.push(createPlane( | |
new Point(startOffset + mSegmentSize * i, 0, 0), | |
new Point(0, -90, 0), | |
"#00f" | |
)); | |
} | |
// y | |
for (i = 0; i <= mDivisions; i++) { | |
mYPlanes.push(createPlane( | |
new Point(0, startOffset + mSegmentSize * i, 0), | |
new Point(90, 0, 0), | |
"#0f0" | |
)); | |
} | |
// z | |
for (i = 0; i <= mDivisions; i++) { | |
mZPlanes.push(createPlane( | |
new Point(0, 0, startOffset + mSegmentSize * i), | |
new Point(), | |
"#f00" | |
)); | |
} | |
} | |
function createPlane(p, r, c) { | |
var plane = new Plane(p, r, c); | |
mAllPlanes.push(plane); | |
mContainer.appendChild(plane.canvas); | |
return plane; | |
} | |
function createTrails() { | |
var x, y, z, position, | |
h, s, l, color; | |
for (var i = 0; i < 8; i++) { | |
x = randomRange(0, mDivisions) | 0; | |
y = randomRange(0, mDivisions) | 0; | |
z = randomRange(0, mDivisions) | 0; | |
position = new Point(x, y, z); | |
h = randomRange(0, 180) | 0; | |
s = randomRange(80, 100) | 0; | |
l = randomRange(80, 100) | 0; | |
color = 'hsl(' + h + ',' + s + '%,' + l + '%)'; | |
startTrailsAt(position, color); | |
} | |
} | |
function startTrailsAt(point, color) { | |
for (var i = 0; i < 6; i++) { | |
var trail = new Trail(point); | |
trail.color = color; | |
mTrails.push(trail); | |
} | |
} | |
function update() { | |
invoke(mTrails, 'update'); | |
} | |
function draw() { | |
invoke(mAllPlanes, 'clear'); | |
invoke(mTrails, 'draw'); | |
} | |
function loop() { | |
update(); | |
draw(); | |
requestAnimationFrame(loop); | |
} | |
function Plane(p, r, c) { | |
this.position = p; | |
this.rotation = r; | |
this.createCanvas(); | |
// moved these here because setting shadow color for each segment turned out to be quite expensive | |
this.ctx.shadowColor = '#fff'; | |
this.ctx.shadowBlur = 16; | |
} | |
Plane.prototype = { | |
createCanvas:function() { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.style.width = mPlaneSize + 'px'; | |
this.canvas.style.height = mPlaneSize + 'px'; | |
this.canvas.width = mPlaneSize; | |
this.canvas.height = mPlaneSize; | |
this.ctx = this.canvas.getContext('2d'); | |
TweenMax.set(this.canvas, { | |
left:-(mPlaneSize * 0.5) + 'px', | |
top:-(mPlaneSize * 0.5) + 'px', | |
x:this.position.x, | |
y:this.position.y, | |
z:this.position.z, | |
rotationX:this.rotation.x, | |
rotationY:this.rotation.y, | |
rotationZ:this.rotation.z | |
}) | |
}, | |
clear:function() { | |
this.ctx.clearRect(0, 0, mPlaneSize, mPlaneSize); | |
} | |
}; | |
function Trail(position) { | |
this.style = { | |
strokeStyle:'#f00', | |
lineWidth:1 | |
}; | |
this.t0 = 0; | |
this.t1 = 0; | |
this.deltaTime = (1/randomRange(5, 20)); | |
this.points = [position || new Point()]; | |
this.maxLength = 3; | |
} | |
Trail.prototype = { | |
set color(c) { | |
this.style.strokeStyle = c; | |
}, | |
update:function() { | |
this.t1 += this.deltaTime; | |
this.t0 = Math.max(0, this.t1 - this.maxLength); | |
if (this.t1 >= this.points.length - 1) { | |
this.appendPoint(); | |
} | |
}, | |
appendPoint:function() { | |
var point = this.points[this.points.length - 1].clone(); | |
var direction = getRandomDirection(); | |
point.x = clamp(point.x + direction.x, 0, mDivisions); | |
point.y = clamp(point.y + direction.y, 0, mDivisions); | |
point.z = clamp(point.z + direction.z, 0, mDivisions); | |
this.points.push(point); | |
}, | |
draw:function() { | |
var p0, p1, start, end; | |
for (var i = 0; i < this.points.length - 1; i++) { | |
p0 = this.points[i]; | |
p1 = this.points[i + 1]; | |
if (this.t0 < i + 1) { | |
if (this.t0 > i) { | |
start = this.t0 - i; | |
} | |
else { | |
start = 0; | |
} | |
if (this.t1 < (i + 1)) { | |
end = this.t1 - i; | |
} | |
else { | |
end = 1; | |
} | |
} | |
this.connectPoints(p0, p1, start, end); | |
} | |
}, | |
connectPoints:function(p0, p1, start, end) { | |
var ctx; | |
var x0 = p0.x + (p1.x - p0.x) * start, | |
y0 = p0.y + (p1.y - p0.y) * start, | |
z0 = p0.z + (p1.z - p0.z) * start; | |
var x1 = p0.x + (p1.x - p0.x) * end, | |
y1 = p0.y + (p1.y - p0.y) * end, | |
z1 = p0.z + (p1.z - p0.z) * end; | |
if (p0.x === p1.x) { | |
ctx = mXPlanes[p0.x].ctx; | |
applyStyle(ctx, this.style); | |
ctx.beginPath(); | |
ctx.moveTo(z0 * mSegmentSize, y0 * mSegmentSize); | |
ctx.lineTo(z1 * mSegmentSize, y1 * mSegmentSize); | |
ctx.stroke(); | |
} | |
if (p0.y === p1.y) { | |
ctx = mYPlanes[p0.y].ctx; | |
applyStyle(ctx, this.style); | |
ctx.beginPath(); | |
ctx.moveTo(x0 * mSegmentSize, z0 * mSegmentSize); | |
ctx.lineTo(x1 * mSegmentSize, z1 * mSegmentSize); | |
ctx.stroke(); | |
} | |
if (p0.z === p1.z) { | |
ctx = mZPlanes[p0.z].ctx; | |
applyStyle(ctx, this.style); | |
ctx.beginPath(); | |
ctx.moveTo(x0 * mSegmentSize, y0 * mSegmentSize); | |
ctx.lineTo(x1 * mSegmentSize, y1 * mSegmentSize); | |
ctx.stroke(); | |
} | |
} | |
}; | |
function Point(x, y, z) { | |
this.x = x || 0; | |
this.y = y || 0; | |
this.z = z || 0; | |
} | |
Point.prototype = { | |
add:function(p) { | |
this.x += p.x; | |
this.y += p.y; | |
this.z += p.z; | |
return this; | |
}, | |
clone:function() { | |
return new Point(this.x, this.y, this.z); | |
} | |
}; | |
function invoke(a, m) { | |
a.forEach(function(el) { | |
el[m].apply(el); | |
}); | |
} | |
function randomRange(min, max) { | |
return min + Math.random() * (max - min); | |
} | |
function wrap(v, min, max) { | |
return (((v - min) % (max - min)) + (max - min)) % (max - min) + min; | |
} | |
function clamp(x, min, max) { | |
return x < min ? min : x > max ? max : x; | |
} | |
function applyStyle(ctx, style) { | |
for (var p in style) { | |
ctx[p] = style[p]; | |
} | |
} | |
getRandomDirection = (function getRandomDirection() { | |
var directions = [ | |
new Point(1, 0, 0), | |
new Point(-1, 0, 0), | |
new Point(0, 1, 0), | |
new Point(0, -1, 0), | |
new Point(0, 0, 1), | |
new Point(0, 0, -1) | |
]; | |
return function() { | |
return directions[Math.floor(Math.random() * directions.length)]; | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: #000; | |
margin: 0; | |
overflow: hidden; | |
} | |
#container { | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
} | |
canvas { | |
position: absolute; | |
background-color: rgba(0,0,0,0.1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment