<!-- Made with Infamous before it was renamed to LUME:
- https://github.com/lume/lume
- https://github.com/tweenjs/tween.js
- https://github.com/bgrins/TinyColor
-->
<script src="https://unpkg.com/infamous@17.0.5/global.js"></script>
<script src="https://unpkg.com/tinycolor2@1.4.1"></script>
<script src="https://unpkg.com/tween.js@16.6.0"></script>
<script>var color = tinycolor</script>
html, body {
width: 100%; height: 100%; padding:0; margin: 0;
}
const {Motor, Scene, Node} = infamous.core
const {Tween, Easing} = TWEEN
const sleep = duration => new Promise(r => setTimeout(r, duration))
async function rippleFlip() {
const mainColor = color('#7ac5de')
// const mainColor = color('#C390D4')
// const mainColor = color('#A1D490')
document.body.style.background = mainColor
const scene = new Scene
scene.mount('body')
const gridSizeX = 13
const gridSizeY = 13
const gridCellSize = 200
const grid = new Node({
absoluteSize: [gridSizeX*gridCellSize, gridSizeY*gridCellSize],
align: [0.5, 0.5],
mountPoint: [0.5, 0.5],
rotation: [30],
position: {z: -600},
})
scene.addChild(grid)
await grid.mountPromise
await sleep(500)
console.log('grid size', grid.actualSize)
const rippleOptions = {
// ripple center
cx: grid.actualSize.x / 2,
cy: grid.actualSize.y / 2,
amountToRotate: 180,
rotationDuration: 1600,
rotationCurve: Easing.Exponential.Out,
amountToDisplace: 200,
displaceDuration: 1600,
//displaceDuration: 2400,
displaceCurve: Easing.Exponential.Out,
amountToOpacify: 1,
opacifyDuration: 2400,
opacifyCurve: Easing.Exponential.Out,
rippleDistance: grid.actualSize.x / 2,
//rippleDuration: 2000,
rippleDuration: 1000,
//rippleCurve: Easing.Exponential.In,
rippleCurve: Easing.Linear.None,
//rippleCurve: Easing.Quadratic.In,
rotation: true,
displacement: true,
opacification: true,
}
// make a grid of rectangles
for (let i=0; i<gridSizeX; i++) {
for (let j=0; j<gridSizeY; j++) {
const node = new Node({
absoluteSize: [gridCellSize,gridCellSize],
position: [i*gridCellSize, j*gridCellSize],
opacity: 0,
})
node.opacity = 0
//node.element.style.background = '#eee'
//node.element.style.border = '1px solid #ccc'
node.element.style.background = ''+mainColor.clone().darken(10)
node.element.style.border = '1px solid ' + mainColor.clone().darken(35)
//node.element.style['backface-visibility'] = 'hidden'
grid.addChild(node)
}
}
await sleep(500)
while (true) {
await ripple(grid, rippleOptions)
await sleep(1000)
}
}
function ripple(grid, {
cx, cy,
amountToRotate, rotationDuration, rotationCurve,
amountToDisplace, displaceDuration, displaceCurve,
amountToOpacify, opacifyDuration, opacifyCurve,
rippleDistance, rippleDuration, rippleCurve,
rotation, displacement, opacification,
}) {
let resolve = null
const promise = new Promise(r => resolve = r)
let radiusTweenComplete = false
const radius = {value:0}
const radiusTween = new Tween(radius)
.to({value:rippleDistance}, rippleDuration)
.easing(rippleCurve)
.onComplete(() => radiusTweenComplete = true)
.start()
Motor.addRenderTask(time => {
radiusTween.update(time)
for (let i = 0, l=grid.children.length; i<l; i+=1) {
const node = grid.children[i]
if (node.animating) continue
if (!node.distanceFromCircle) {
const dx = cx - (node.position.x + 50)
const dy = cy - (node.position.y + 50)
const distanceToCircleCenter = Math.sqrt(dx**2 + dy**2)
node.initialDistanceFromCircle = distanceToCircleCenter - radius.value
node.distanceFromCircle = node.initialDistanceFromCircle
}
else {
node.distanceFromCircle = node.initialDistanceFromCircle - radius.value
}
if (node.distanceFromCircle <= 0) {
node.animating = true
if (rotation) rotateNode(node, amountToRotate, rotationDuration, rotationCurve)
if (displacement) displaceNode(node, amountToDisplace, displaceDuration, displaceCurve)
if (opacification) opacifyNode(node, amountToOpacify, opacifyDuration, opacifyCurve)
}
}
if (radiusTweenComplete) {
const children = grid.children
for (let i = 0, l=children.length; i<l; i+=1) {
children[i].animating = false
}
resolve()
return false
}
})
return promise
}
function rotateNode(node, finalValue, duration, curve) {
let resolve = null
const promise = new Promise(r => resolve = r)
let tweenDone = false
const rotationTween = new Tween(node.rotation)
.to({y:'+180'}, duration)
.easing(curve)
.onComplete(() => tweenDone = true)
.start()
Motor.addRenderTask(time => {
rotationTween.update(time)
if (tweenDone) {
resolve()
return false
}
})
return promise
}
function displaceNode(node, amount, duration, curve) {
let resolve = null
const promise = new Promise(r => resolve = r)
const displace = {value: 0}
let tweenDone = false
const displacementTween = new Tween(displace)
.to({value: Math.PI}, duration)
.easing(curve)
.onComplete(() => tweenDone = true)
.start()
Motor.addRenderTask(time => {
displacementTween.update(time)
node.position.z = amount * Math.sin(displace.value)
if (tweenDone) {
resolve()
return false
}
})
return promise
}
function opacifyNode(node, amount, duration, curve) {
let resolve = null
const promise = new Promise(r => resolve = r)
const opacify = {value: 0}
let tweenDone = false
const opacifyTween = new Tween(opacify)
.to({value: Math.PI}, duration)
.easing(curve)
.onComplete(() => tweenDone = true)
.start()
Motor.addRenderTask(time => {
opacifyTween.update(time)
node.opacity = amount * Math.sin(opacify.value)
if (tweenDone) {
resolve()
return false
}
})
return promise
}
rippleFlip()
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.