Teleport out effect
This commit is contained in:
+65
-6
@@ -293,20 +293,21 @@ window.addEventListener('scroll', function () {
|
|||||||
document.body.appendChild(s);
|
document.body.appendChild(s);
|
||||||
s.animate([
|
s.animate([
|
||||||
{ transform: 'translate(-50%,-50%) scale(0)' },
|
{ transform: 'translate(-50%,-50%) scale(0)' },
|
||||||
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.3 },
|
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.08 }, // pop in almost instantly
|
||||||
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.7 },
|
{ transform: 'translate(-50%,-50%) scale(' + size + ')', offset: 0.6 },
|
||||||
{ transform: 'translate(-50%,-50%) scale(0)' }
|
{ transform: 'translate(-50%,-50%) scale(0)' }
|
||||||
], { duration: life, easing: 'ease-in-out' }).onfinish = function () { s.remove(); };
|
], { duration: life, easing: 'ease-out' }).onfinish = function () { s.remove(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
function teleport(img) {
|
function teleport(img, startDelay) {
|
||||||
var r = img.getBoundingClientRect();
|
var r = img.getBoundingClientRect();
|
||||||
var START_DELAY = 500; // pause before anything happens
|
var START_DELAY = (typeof startDelay === 'number') ? startDelay : 500; // pause before anything happens
|
||||||
var CHARGE = 1000; // sparkles gather along the bottom before the figure grows
|
var CHARGE = 1000; // sparkles gather along the bottom before the figure grows
|
||||||
var GROW = 2200; // the bottom-up materialise
|
var GROW = 2200; // the bottom-up materialise
|
||||||
var baseShadow = 'drop-shadow(0 8px 24px rgba(0,0,0,0.35))';
|
var baseShadow = 'drop-shadow(0 8px 24px rgba(0,0,0,0.35))';
|
||||||
function ease(t) { return t * t; } // ease-in: gentle at the bottom, no slow-down toward the top
|
function ease(t) { return t * t; } // ease-in: gentle at the bottom, no slow-down toward the top
|
||||||
|
|
||||||
|
img.dataset.teleBusy = '1';
|
||||||
// Keep it invisible (clipped) but opaque from the outset, so nothing flashes during the delay
|
// Keep it invisible (clipped) but opaque from the outset, so nothing flashes during the delay
|
||||||
img.style.opacity = '1';
|
img.style.opacity = '1';
|
||||||
img.style.webkitClipPath = img.style.clipPath = 'inset(100% 0 0 0)';
|
img.style.webkitClipPath = img.style.clipPath = 'inset(100% 0 0 0)';
|
||||||
@@ -369,16 +370,74 @@ window.addEventListener('scroll', function () {
|
|||||||
img.style.filter = ''; // CSS drop-shadow returns
|
img.style.filter = ''; // CSS drop-shadow returns
|
||||||
img.classList.remove('mascot-teleport');
|
img.classList.remove('mascot-teleport');
|
||||||
img.style.opacity = '1';
|
img.style.opacity = '1';
|
||||||
|
delete img.dataset.teleBusy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestAnimationFrame(frame);
|
requestAnimationFrame(frame);
|
||||||
}, START_DELAY);
|
}, START_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reverse "beam out": dissolve top-down into a puff of sparkles, then stay hidden
|
||||||
|
function dematerialize(img, done) {
|
||||||
|
var r = img.getBoundingClientRect();
|
||||||
|
var DUR = 800;
|
||||||
|
var baseShadow = 'drop-shadow(0 8px 24px rgba(0,0,0,0.35))';
|
||||||
|
var start = null;
|
||||||
|
function frame(ts) {
|
||||||
|
if (!start) start = ts;
|
||||||
|
var p = Math.min((ts - start) / DUR, 1);
|
||||||
|
|
||||||
|
// Dissolve away from the top downwards
|
||||||
|
img.style.webkitClipPath = img.style.clipPath = 'inset(' + (p * 100).toFixed(2) + '% 0 0 0)';
|
||||||
|
|
||||||
|
// Energy flash that builds as it beams out
|
||||||
|
var bright = (1 + 1.2 * p).toFixed(2);
|
||||||
|
var glow = (0.7 * p).toFixed(2);
|
||||||
|
img.style.filter = baseShadow + ' drop-shadow(0 0 14px rgba(255,255,255,' + glow + ')) brightness(' + bright + ')';
|
||||||
|
|
||||||
|
// Sparkles riding the dissolving edge
|
||||||
|
var lineY = r.top + p * r.height;
|
||||||
|
if (p < 0.98) {
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
var x = r.left + Math.random() * r.width;
|
||||||
|
var y = lineY + (Math.random() - 0.5) * r.height * 0.1;
|
||||||
|
y = Math.max(r.top, Math.min(r.bottom, y));
|
||||||
|
teleSpark(x, y, 480 + Math.random() * 360);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p < 1) {
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
} else {
|
||||||
|
// A final puff of sparkles where it stood
|
||||||
|
for (var j = 0; j < 16; j++) {
|
||||||
|
teleSpark(r.left + Math.random() * r.width, r.top + Math.random() * r.height, 420 + Math.random() * 360);
|
||||||
|
}
|
||||||
|
img.style.opacity = '0'; // stay hidden during the wait
|
||||||
|
img.style.webkitClipPath = img.style.clipPath = '';
|
||||||
|
img.style.filter = '';
|
||||||
|
if (done) done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
var io = new IntersectionObserver(function (entries) {
|
var io = new IntersectionObserver(function (entries) {
|
||||||
entries.forEach(function (e) {
|
entries.forEach(function (e) {
|
||||||
if (e.isIntersecting) { io.unobserve(e.target); teleport(e.target); }
|
if (e.isIntersecting) { io.unobserve(e.target); teleport(e.target); }
|
||||||
});
|
});
|
||||||
}, { threshold: 0.25 });
|
}, { threshold: 0.25 });
|
||||||
imgs.forEach(function (im) { io.observe(im); });
|
|
||||||
|
imgs.forEach(function (im) {
|
||||||
|
io.observe(im);
|
||||||
|
// Click to beam out, then re-materialise after ~3s
|
||||||
|
im.style.cursor = 'pointer';
|
||||||
|
im.addEventListener('click', function () {
|
||||||
|
if (im.dataset.teleBusy) return; // ignore while an effect is already running
|
||||||
|
im.dataset.teleBusy = '1';
|
||||||
|
dematerialize(im, function () {
|
||||||
|
setTimeout(function () { teleport(im, 0); }, 3000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user