I tried to make a gallery, but the images keep shattering when I click on them... I'll need to use more sturdy materials next time.
(Delaunay triangulation part 2)
A Pen by Szenia Zadvornykh on CodePen.
<div id="container"></div> | |
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/delaunay.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.13.2/TweenMax.min.js"></script> |
// triangulation using https://github.com/ironwallaby/delaunay | |
const TWO_PI = Math.PI * 2; | |
var images = [], | |
imageIndex = 0; | |
var image, | |
imageWidth = 768, | |
imageHeight = 485; | |
var vertices = [], | |
indices = [], | |
fragments = []; | |
var container = document.getElementById('container'); | |
var clickPosition = [imageWidth * 0.5, imageHeight * 0.5]; | |
window.onload = function() { | |
TweenMax.set(container, {perspective:500}); | |
// images from reddit/r/wallpapers | |
var urls = [ | |
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/crayon.jpg', | |
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/spaceship.jpg', | |
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/dj.jpg', | |
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/chicken.jpg' | |
], | |
image, | |
loaded = 0; | |
// very quick and dirty hack to load and display the first image asap | |
images[0] = image = new Image(); | |
image.onload = function() { | |
if (++loaded === 1) { | |
imagesLoaded(); | |
for (var i = 1; i < 4; i++) { | |
images[i] = image = new Image(); | |
image.src = urls[i]; | |
} | |
} | |
}; | |
image.src = urls[0]; | |
}; | |
function imagesLoaded() { | |
placeImage(false); | |
triangulate(); | |
shatter(); | |
} | |
function placeImage(transitionIn) { | |
image = images[imageIndex]; | |
if (++imageIndex === images.length) imageIndex = 0; | |
image.addEventListener('click', imageClickHandler); | |
container.appendChild(image); | |
if (transitionIn !== false) { | |
TweenMax.fromTo(image, 0.75, {y:-1000}, {y:0, ease:Back.easeOut}); | |
} | |
} | |
function imageClickHandler(event) { | |
var box = image.getBoundingClientRect(), | |
top = box.top, | |
left = box.left; | |
clickPosition[0] = event.clientX - left; | |
clickPosition[1] = event.clientY - top; | |
triangulate(); | |
shatter(); | |
} | |
function triangulate() { | |
var rings = [ | |
{r:50, c:12}, | |
{r:150, c:12}, | |
{r:300, c:12}, | |
{r:1200, c:12} // very large in case of corner clicks | |
], | |
x, | |
y, | |
centerX = clickPosition[0], | |
centerY = clickPosition[1]; | |
vertices.push([centerX, centerY]); | |
rings.forEach(function(ring) { | |
var radius = ring.r, | |
count = ring.c, | |
variance = radius * 0.25; | |
for (var i = 0; i < count; i++) { | |
x = Math.cos((i / count) * TWO_PI) * radius + centerX + randomRange(-variance, variance); | |
y = Math.sin((i / count) * TWO_PI) * radius + centerY + randomRange(-variance, variance); | |
vertices.push([x, y]); | |
} | |
}); | |
vertices.forEach(function(v) { | |
v[0] = clamp(v[0], 0, imageWidth); | |
v[1] = clamp(v[1], 0, imageHeight); | |
}); | |
indices = Delaunay.triangulate(vertices); | |
} | |
function shatter() { | |
var p0, p1, p2, | |
fragment; | |
var tl0 = new TimelineMax({onComplete:shatterCompleteHandler}); | |
for (var i = 0; i < indices.length; i += 3) { | |
p0 = vertices[indices[i + 0]]; | |
p1 = vertices[indices[i + 1]]; | |
p2 = vertices[indices[i + 2]]; | |
fragment = new Fragment(p0, p1, p2); | |
var dx = fragment.centroid[0] - clickPosition[0], | |
dy = fragment.centroid[1] - clickPosition[1], | |
d = Math.sqrt(dx * dx + dy * dy), | |
rx = 30 * sign(dy), | |
ry = 90 * -sign(dx), | |
delay = d * 0.003 * randomRange(0.9, 1.1); | |
fragment.canvas.style.zIndex = Math.floor(d).toString(); | |
var tl1 = new TimelineMax(); | |
tl1.to(fragment.canvas, 1, { | |
z:-500, | |
rotationX:rx, | |
rotationY:ry, | |
ease:Cubic.easeIn | |
}); | |
tl1.to(fragment.canvas, 0.4,{alpha:0}, 0.6); | |
tl0.insert(tl1, delay); | |
fragments.push(fragment); | |
container.appendChild(fragment.canvas); | |
} | |
container.removeChild(image); | |
image.removeEventListener('click', imageClickHandler); | |
} | |
function shatterCompleteHandler() { | |
// add pooling? | |
fragments.forEach(function(f) { | |
container.removeChild(f.canvas); | |
}); | |
fragments.length = 0; | |
vertices.length = 0; | |
indices.length = 0; | |
placeImage(); | |
} | |
////////////// | |
// MATH UTILS | |
////////////// | |
function randomRange(min, max) { | |
return min + (max - min) * Math.random(); | |
} | |
function clamp(x, min, max) { | |
return x < min ? min : (x > max ? max : x); | |
} | |
function sign(x) { | |
return x < 0 ? -1 : 1; | |
} | |
////////////// | |
// FRAGMENT | |
////////////// | |
Fragment = function(v0, v1, v2) { | |
this.v0 = v0; | |
this.v1 = v1; | |
this.v2 = v2; | |
this.computeBoundingBox(); | |
this.computeCentroid(); | |
this.createCanvas(); | |
this.clip(); | |
}; | |
Fragment.prototype = { | |
computeBoundingBox:function() { | |
var xMin = Math.min(this.v0[0], this.v1[0], this.v2[0]), | |
xMax = Math.max(this.v0[0], this.v1[0], this.v2[0]), | |
yMin = Math.min(this.v0[1], this.v1[1], this.v2[1]), | |
yMax = Math.max(this.v0[1], this.v1[1], this.v2[1]); | |
this.box ={ | |
x:xMin, | |
y:yMin, | |
w:xMax - xMin, | |
h:yMax - yMin | |
}; | |
}, | |
computeCentroid:function() { | |
var x = (this.v0[0] + this.v1[0] + this.v2[0]) / 3, | |
y = (this.v0[1] + this.v1[1] + this.v2[1]) / 3; | |
this.centroid = [x, y]; | |
}, | |
createCanvas:function() { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.width = this.box.w; | |
this.canvas.height = this.box.h; | |
this.canvas.style.width = this.box.w + 'px'; | |
this.canvas.style.height = this.box.h + 'px'; | |
this.canvas.style.left = this.box.x + 'px'; | |
this.canvas.style.top = this.box.y + 'px'; | |
this.ctx = this.canvas.getContext('2d'); | |
}, | |
clip:function() { | |
this.ctx.translate(-this.box.x, -this.box.y); | |
this.ctx.beginPath(); | |
this.ctx.moveTo(this.v0[0], this.v0[1]); | |
this.ctx.lineTo(this.v1[0], this.v1[1]); | |
this.ctx.lineTo(this.v2[0], this.v2[1]); | |
this.ctx.closePath(); | |
this.ctx.clip(); | |
this.ctx.drawImage(image, 0, 0); | |
} | |
}; |
I tried to make a gallery, but the images keep shattering when I click on them... I'll need to use more sturdy materials next time.
(Delaunay triangulation part 2)
A Pen by Szenia Zadvornykh on CodePen.
body { | |
background-color: #000; | |
margin: 0; | |
overflow: hidden; | |
} | |
canvas { | |
position: absolute; | |
backface-visibility: hidden; | |
-webkit-backface-visibility: hidden; | |
-moz-backface-visibility: hidden; | |
-ms-backface-visibility: hidden; | |
} | |
img { | |
position: absolute; | |
cursor: pointer; | |
} | |
#container { | |
position: absolute; | |
width: 768px; | |
height: 485px; | |
left: 0; | |
right: 0; | |
top: 0; | |
bottom: 0; | |
margin: auto; | |
} |