Hi all, in a nextjs app Im using requestAnimationFrame and matter.js to animate shapes created using paper.js. The shapes are created with a javascript Class inside a forEach loop. Each shape (RegularPolygon) has a random number of sides. When I run the code, Matter.js takes care of the physics but the logic to create the random sides also animates creating a cool (but undesirable strobing effect) of continuously randomising the number of sides of the RegularPolygon. I'm looking for a way of creating the shapes with random sides, once, and then animating them. Thanks in advance.
link to netlify site
import { Engine, Render, World, Bodies } from 'matter-js';
import { useEffect, useRef } from 'react';
import { paper } from 'paper';
const random = (min, max) => {
return Math.random() * min + Math.random() * max + min;
};
function Canvas(props) {
const scene = useRef();
const engine = useRef(Engine.create());
const paperRef = useRef();
const raf = useRef();
console.log(random);
useEffect(() => {
const time = 0;
window.addEventListener('load', () => {
paper.setup(paperRef.current);
const cw = document.body.clientWidth;
const ch = document.body.clientHeight;
const render = Render.create({
element: scene.current,
engine: engine.current,
options: {
width: cw,
height: ch,
wireframes: false,
background: 'transparent',
},
});
World.add(engine.current.world, [
Bodies.rectangle(cw / 2, -10, cw, 20, {
isStatic: true,
}),
Bodies.rectangle(-10, ch / 2, 20, ch, {
isStatic: true,
fillStyle: '#F35e66',
}),
Bodies.rectangle(cw / 2, ch + 10, cw, 20, {
isStatic: true,
fillStyle: '#F35e66',
}),
Bodies.rectangle(cw + 10, ch / 2, 20, ch, {
isStatic: true,
fillStyle: '#F35e66',
}),
]);
Engine.run(engine.current);
Render.run(render);
const balls = [];
for (let i = 0; i < 40; i++) {
balls.push(
Bodies.circle(Math.random() * cw, 0, 80, {
density: Math.random(),
friction: 0.01,
frictionAir: 0.00001,
restitution: 0.8,
render: {
fillStyle: '#F35e66',
strokeStyle: 'black',
lineWidth: 1,
},
})
);
}
var ball = Bodies.circle(0, 0, 20, {
density: 0.04,
friction: 0.01,
frictionAir: 0.00001,
restitution: 0.8,
render: {
fillStyle: '#F35e66',
strokeStyle: 'black',
lineWidth: 1,
},
});
for (let i = 0; i < balls.length; i++) {
World.add(engine.current.world, [balls[i]]);
}
const shapes = [];
for (let i = 0; i < balls.length; i++) {
shapes.push(
new paper.Path.RegularPolygon({
position: new paper.Point([
balls[i].position.x,
balls[i].position.y,
]),
sides: Math.floor(random(0, 8)),
radius: 100,
fillColor: 'tomato',
})
);
}
class Shape {
constructor(x, y, angle) {
this.angle = angle;
this.x = x;
this.y = y;
this.draw();
this.shape = new paper.Path.RegularPolygon({
position: new paper.Point([this.x, this.y]),
sides: Math.floor(random(0, 8)),
radius: 100,
fillColor: 'tomato',
});
}
draw() {
// this.shape.rotate(this.angle);
}
}
console.log(ball);
const callback = () => {
// paper.view.update();
paper.project.clear();
balls.forEach((ball) => {
new Shape(ball.position.x, ball.position.y, ball.angle);
// let shape = new paper.Path.RegularPolygon({
// position: new paper.Point([
// ball.position.x,
// ball.position.y,
// ]),
// sides: Math.floor(Math.random() * 8),
// radius: 100,
// fillColor: 'tomato',
// });
// shape.rotate(ball.angle);
});
const shape = new paper.Path.RegularPolygon({
position: new paper.Point([
ball.position.x,
ball.position.y,
]),
sides: 5,
radius: 100,
fillColor: 'tomato',
});
shape.rotate(ball.angle);
raf.current = requestAnimationFrame(callback);
};
raf.current = requestAnimationFrame(callback);
return () => {
cancelAnimationFrame(raf.current);
Render.stop(render);
World.clear(engine.current.world);
Engine.clear(engine.current);
render.canvas.remove();
render.canvas = null;
render.context = null;
render.textures = {};
};
});
}, []);
return (
<div className=''>
<div className='paper'>
<canvas
resize='true'
ref={paperRef}
style={{
width: '100%',
height: '100%',
position: 'fixed',
top: 0,
left: 0,
}}
/>
</div>
<div
ref={scene}
style={{
display: 'none',
width: '100%',
height: '100%',
position: 'fixed',
top: 0,
left: 0,
}}
/>
</div>
);
}
export default Canvas;
I just know how to use for to draw a tree (the tree data is the picture one, the result is picture two), but now I want to use recursion to draw the tree.
Please tell me how change writing style from for to recursive
first input point
//input point
const line_point =[0, 0, 0,
2, 151, 2,
2, 151, 2,
-62, 283, 63,
2, 151, 2,
62, 297, -58,
-62, 283, 63,
-104, 334, 74,
-62, 283, 63,
-58, 338, 45,
62, 297, -58,
67, 403, -55,
62, 297, -58,
105, 365, -86];
take out star point and end point
const star_line_x= new Array();
const star_line_y= new Array();
const star_line_z= new Array();
const end_line_x= new Array();
const end_line_y= new Array();
const end_line_z= new Array();
for (var q=0; q < line_point.length; q+=6){
star_line_x.push(line_point[q]);
}
for (var r=1; r < line_point.length; r+=6){
star_line_y.push(line_point[r]);
}
for (var s=2; s < line_point.length; s+=6){
star_line_z.push(line_point[s]);
}
for (var t=3; t < line_point.length; t+=6){
end_line_x.push(line_point[t]);
}
for (var u=4; u < line_point.length; u+=6){
end_line_y.push(line_point[u]);
}
for (var v=5; v < line_point.length; v+=6){
end_line_z.push(line_point[v]);
}
var cylinder_star_point = new Array();
var cylinder_end_point = new Array();
//star_point end_point
for (var w=0; w < line_point.length/6; w++){
var star_point = new THREE.Vector3 (star_line_x[w],star_line_y[w],star_line_z[w]);
var end_point = new THREE.Vector3 (end_line_x[w],end_line_y[w],end_line_z[w]);
cylinder_star_point.push( star_point);
cylinder_end_point.push( end_point);
}
calculation cylinder high
//calculation cylinder high
var line_len = new Array();
for (var dd=0; dd < line_point.length/6; dd++){
var len_x = Math.pow(end_line_x[dd]-star_line_x[dd],2);
var len_y = Math.pow(end_line_y[dd]-star_line_y[dd],2);
var len_z = Math.pow(end_line_z[dd]-star_line_z[dd],2);
var len_direction = Math.sqrt(len_x+len_y+len_z);
line_len.push(len_direction);//Cylinder high
}
calculation center point
//center_point
const cylinder_center_point= new Array();
for (var bb=0; bb< cylinder_end_point.length; bb++){
var star_set_point = cylinder_star_point[bb];
var end_set_point = cylinder_end_point[bb];
var center_point = end_set_point.clone().add(star_set_point).divideScalar(2);
cylinder_center_point.push(center_point);
}
calculation cylinder direction vector
//cylinder direction
const cylinder_direction= new Array();
for (var cc=0; cc < cylinder_end_point.length; cc++){
var star_direction = cylinder_star_point[cc];
var end_direction = cylinder_end_point[cc];
var center_direction = end_direction.clone().sub(star_direction);
cylinder_direction.push(center_direction);
}
draw cylinder
for (var dd=0; dd <cylinder_direction.length;dd++){
var material = new THREE.MeshPhongMaterial({color:'#ff0000'});
let upVector = new THREE.Vector3(0, 1, 0);
var geometry = new THREE.CylinderGeometry(5, 5, line_len[dd], 20, 1, false);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, line_len[dd]/2, 0);
var group = new THREE.Group();
group.position.set(star_line_x[dd],star_line_y[dd],star_line_z[dd]);
group.add(mesh);
let targetVector =cylinder_direction[dd];
let quaternion = new THREE.Quaternion().setFromUnitVectors(upVector, targetVector.normalize());
group.setRotationFromQuaternion(quaternion)
scene.add(group)
}
picture two: use for to draw the tree
For a tree the simplest method is to start with just a tree depth and assume 2 children. The function makes one branch and if depth > 0 then it recursively calls itself to make 2 more branches.
const numBranches = 2;
const spread = 1.5;
const branchShrinkFactor = 0.8;
const branchSpreadFactor = 0.8;
function addBranch(parent, depth, offset, angle, branchLength, spread) {
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderBufferGeometry(5, 5, branchLength, 20, 1, false);
geometry.translate(0, branchLength / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
if (depth > 1) {
for (let i = 0; i < numBranches; ++i) {
const a = i / (numBranches - 1) - 0.5;
addBranch(mesh, depth - 1, branchLength, a * spread, branchLength * branchShrinkFactor, spread * branchSpreadFactor)
}
}
}
addBranch(scene, 5, 0, 0, 100, 1.5);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 150, 300);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const numBranches = 2;
const spread = 1.5;
const branchShrinkFactor = 0.8;
const branchSpreadFactor = 0.8;
function addBranch(parent, depth, offset, angle, branchLength, spread) {
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderBufferGeometry(5, 5, branchLength, 20, 1, false);
geometry.translate(0, branchLength / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
if (depth > 1) {
for (let i = 0; i < numBranches; ++i) {
const a = i / (numBranches - 1) - 0.5;
addBranch(mesh, depth - 1, branchLength, a * spread, branchLength * branchShrinkFactor, spread * branchSpreadFactor)
}
}
}
addBranch(scene, 5, 0, 0, 100, 1.5);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
If you want specific data for each branch then you need to pass that in. For example
const tree = [
{ length: 100, angle: 0, branches: 2 }, // root
{ length: 40, angle: -1, branches: 3 }, // first branch
{ length: 50, angle: 0.8, branches: 0 }, // 1st child branch
{ length: 40, angle: 0.3, branches: 0 }, // 2nd child branch
{ length: 30, angle: -0.3, branches: 0 }, // 3rd child branch
{ length: 50, angle: 0.8, branches: 2 }, // second branch
{ length: 50, angle: 0.5, branches: 0 }, // 1st child branch
{ length: 40, angle: -0.6, branches: 2 }, // 2nd child branch
{ length: 40, angle: -0.3, branches: 0 }, // 1st grandchild branch
{ length: 95, angle: 0.3, branches: 0 }, // 2st grandchild branch
];
and then walk the tree description, if a branches for a particular branch is > 0 then it recursively calls itself to add those branches. Each branches consumes a row in the array of branches so we pass back ndx so we can tell how many rows were consumed.
function addBranch(parent, offset, tree, ndx = 0) {
const {length, angle, branches} = tree[ndx];
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
for (let i = 0; i < branches; ++i) {
ndx = addBranch(mesh, length, tree, ++ndx);
}
return ndx;
}
addBranch(scene, 0, tree);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 150, 300);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const tree = [
{ length: 100, angle: 0, branches: 2 }, // root
{ length: 40, angle: -1, branches: 3 }, // first branch
{ length: 50, angle: 0.8, branches: 0 }, // 1st child branch
{ length: 40, angle: 0.3, branches: 0 }, // 2nd child branch
{ length: 30, angle: -0.3, branches: 0 }, // 3rd child branch
{ length: 50, angle: 0.8, branches: 2 }, // second branch
{ length: 50, angle: 0.5, branches: 0 }, // 1st child branch
{ length: 40, angle: -0.6, branches: 2 }, // 2nd child branch
{ length: 40, angle: -0.3, branches: 0 }, // 1st grandchild branch
{ length: 95, angle: 0.3, branches: 0 }, // 2st grandchild branch
];
function addBranch(parent, offset, tree, ndx = 0) {
const {length, angle, branches} = tree[ndx];
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = offset;
mesh.rotation.z = angle;
parent.add(mesh);
for (let i = 0; i < branches; ++i) {
ndx = addBranch(mesh, length, tree, ++ndx);
}
return ndx;
}
addBranch(scene, 0, tree);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
It's not clear to me what your input data is. Your tree has a depth of 3 and 2 branches per level so this data would work
const endPoints = [
[ 0, 0, 0], // A
[ 2, 151, 2], // B
[ -62, 283, 63], // C
[-104, 334, 74], // E
[ -58, 338, 45], // F
[ 62, 296, -58], // D
[ 67, 403, -55], // G
[ 105, 365, -86], // H
];
using this code
// assumes there are 2 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 2;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree);
I pointed the cylinders in the positive Z direction which means I can use lookAt to point the cylinder from its start to its end point.
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(250, 170, 250);
camera.lookAt(0, 170, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const tree = [
[ 0, 0, 0], // A
[ 2, 151, 2], // B
[ -62, 283, 63], // C
[-104, 334, 74], // E
[ -58, 338, 45], // F
[ 62, 296, -58], // D
[ 67, 403, -55], // G
[ 105, 365, -86], // H
].map(v => new THREE.Vector3().fromArray(v));
// assumes there are 2 branches per
function addBranch(parent, depth, offset, tree, parentNdx = 0, childNdx = 1) {
const start = tree[parentNdx];
const end = tree[childNdx];
const length = start.distanceTo(end);
const material = new THREE.MeshPhongMaterial({color:'#ff0000'});
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
let ndx = childNdx + 1;
if (depth > 1) {
const numBranches = 2;
for (let i = 0; i < numBranches; ++i) {
ndx = addBranch(mesh, depth - 1, length, tree, childNdx, ndx);
}
}
return ndx;
}
addBranch(scene, 3, 0, tree);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
note: this only one of infinite ways to create the tree recursively. Rather than an array in depth first order you could also create a tree structure to pass into the algorithm
const E = {
pos: [-104, 334, 74],
};
const F = {
pos: [ -58, 338, 45],
};
const C = {
pos: [ -62, 283, 63],
children: [E, F],
};
const G = {
pos: [ 67, 403, -55],
};
const H = {
pos: [ 105, 365, -86],
};
const D = {
pos: [ 62, 296, -58],
children: [G, H],
};
const B = {
pos: [ 2, 151, 2],
children: [C, D],
};
const A = {
pos: [0, 0, 0],
children: [B],
};
function addBranch(parent, branch, offset = 0) {
const {pos, children} = branch;
const start = new THREE.Vector3().fromArray(pos);
for (const child of children) {
const end = new THREE.Vector3().fromArray(child.pos);
const length = start.distanceTo(end);
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
if (child.children) {
addBranch(mesh, child, length);
}
}
}
addBranch(scene, A);
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(250, 170, 250);
camera.lookAt(0, 170, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightskyblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const E = {
pos: [-104, 334, 74],
};
const F = {
pos: [ -58, 338, 45],
};
const C = {
pos: [ -62, 283, 63],
children: [E, F],
};
const G = {
pos: [ 67, 403, -55],
};
const H = {
pos: [ 105, 365, -86],
};
const D = {
pos: [ 62, 296, -58],
children: [G, H],
};
const B = {
pos: [ 2, 151, 2],
children: [C, D],
};
const A = {
pos: [0, 0, 0],
children: [B],
};
function addBranch(parent, branch, offset = 0) {
const {pos, children} = branch;
const start = new THREE.Vector3().fromArray(pos);
for (const child of children) {
const end = new THREE.Vector3().fromArray(child.pos);
const length = start.distanceTo(end);
const geometry = new THREE.CylinderGeometry(5, 5, length, 20, 1, false);
geometry.translate(0, length / 2, 0);
geometry.rotateX(Math.PI / 2);
const material = new THREE.MeshPhongMaterial({color: 'red'});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.z = offset;
parent.add(mesh);
mesh.lookAt(end);
if (child.children) {
addBranch(mesh, child, length);
}
}
}
addBranch(scene, A);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
// requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
I am searching for a script that creates a glass break effect with text using CSS but I didn't find anything.
I used this but
// triangulation using https://github.com/ironwallaby/delaunay
// For more check out zachsaucier.com
const TWO_PI = Math.PI * 2;
var images = [],
imageIndex = 0;
var image,
imageWidth = 50,
imageHeight = 50;
var vertices = [],
indices = [],
prevfrag = [],
fragments = [];
var margin = 50;
var container = document.getElementById('container');
var clickPosition = [imageWidth * 0.5, imageHeight * 0.5];
window.onload = function() {
TweenMax.set(container, {
perspective: 500
});
// images from http://www.hdwallpapers.in
var urls = [
'http://i.imgur.com/QddsEpk.jpg',
'http://i.imgur.com/OeDykaH.jpg',
'http://i.imgur.com/lLHspCj.jpg',
'http://i.imgur.com/tCz9GQS.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) {
for (var i = 1; i < 4; i++) {
images[i] = image = new Image();
image.src = urls[i];
}
placeImage();
}
};
image.src = urls[0];
};
function placeImage(transitionIn) {
image = images[imageIndex];
if (++imageIndex === images.length) imageIndex = 0;
var num = Math.random();
if (num < .25) {
image.direction = "left";
} else if (num < .5) {
image.direction = "top";
} else if (num < .75) {
image.direction = "bottom";
} else {
image.direction = "right";
}
container.appendChild(image);
image.style.opacity = 0;
if (transitionIn !== false) {
triangulateIn();
}
}
function triangulateIn(event) {
var box = image.getBoundingClientRect(),
top = box.top,
left = box.left;
if (image.direction == "left") {
clickPosition[0] = 0;
clickPosition[1] = imageHeight / 2;
} else if (image.direction == "top") {
clickPosition[0] = imageWidth / 2;
clickPosition[1] = 0;
} else if (image.direction == "bottom") {
clickPosition[0] = imageWidth / 2;
clickPosition[1] = imageHeight;
} else if (image.direction == "right") {
clickPosition[0] = imageWidth;
clickPosition[1] = imageHeight / 2;
}
triangulate();
build();
}
function triangulate() {
for (var i = 0; i < 40; i++) {
x = -margin + Math.random() * (imageWidth + margin * 2);
y = -margin + Math.random() * (imageHeight + margin * 2);
vertices.push([x, y]);
}
vertices.push([0, 0]);
vertices.push([imageWidth, 0]);
vertices.push([imageWidth, imageHeight]);
vertices.push([0, imageHeight]);
vertices.forEach(function(v) {
v[0] = clamp(v[0], 0, imageWidth);
v[1] = clamp(v[1], 0, imageHeight);
});
indices = Delaunay.triangulate(vertices);
}
function build() {
var p0, p1, p2,
fragment;
var tl0 = new TimelineMax({
onComplete: buildCompleteHandler
});
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();
if (image.direction == "left") {
rx = Math.abs(rx);
ry = 0;
} else if (image.direction == "top") {
rx = 0;
ry = Math.abs(ry);
} else if (image.direction == "bottom") {
rx = 0;
ry = -Math.abs(ry);
} else if (image.direction == "right") {
rx = -Math.abs(rx);
ry = 0;
}
tl1.from(fragment.canvas, 1, {
z: -50,
rotationX: rx,
rotationY: ry,
scaleX: 0,
scaleY: 0,
ease: Cubic.easeIn
});
tl1.from(fragment.canvas, 0.4, {
alpha: 0
}, 0.6);
tl0.insert(tl1, delay);
fragments.push(fragment);
container.appendChild(fragment.canvas);
}
}
function buildCompleteHandler() {
// add pooling?
image.style.opacity = 1;
image.addEventListener('transitionend', function catchTrans() {
fragments.forEach(function(f) {
container.removeChild(f.canvas);
});
fragments.length = 0;
vertices.length = 0;
indices.length = 0;
placeImage();
this.removeEventListener('transitionend', catchTrans, false);
}, false);
}
//////////////
// 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;
} < script >
//////////////
// 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: Math.round(xMin),
y: Math.round(yMin),
w: Math.round(xMax - xMin),
h: Math.round(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.save();
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);
this.ctx.restore();
}
};
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;
-webkit-transition: opacity .3s;
transition: opacity .3s;
}
#container {
position: absolute;
width: 25px;
height: 25px;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
<div id="container"></div>
Main code for this is from here but I want glassbreak like this using CSS
So while I don't have a specific example of glass breaking with CSS3, I do recommend this kind of effect would probably work very well with glass breaking:
http://www.species-in-pieces.com/
As you notice they use CSS3 polygons to render out shapes of animals. Here's an example snip from the site:
-webkit-clip-path: polygon(44.65% 39.571%, 35.85% 59.429%, 52.85% 50.857%);
-webkit-transition-duration: .8s;
-moz-transition-duration: .8s;
transition-duration: .8s;
Essentially you'd define webkit transforms through each defined polygon for the effect. Drawbacks of using a feature like this is it's currently only supported in webkit browsers, but at the same time that kind of animation affect would be pretty hard to do cross browser support for in CSS.
If I have some time I'll come around and do a quick glass breaking fiddle tomorrow