410 lines
11 KiB
HTML
410 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>time control • anime.js</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
|
<meta property="og:title" content="anime.js">
|
|
<meta property="og:url" content="https://animejs.com">
|
|
<meta property="og:description" content="Javascript Animation Engine">
|
|
<meta property="og:image" content="https://animejs.com/documentation/assets/img/icons/og.png">
|
|
<meta name="twitter:card" content="summary">
|
|
<meta name="twitter:title" content="anime.js">
|
|
<meta name="twitter:site" content="@juliangarnier">
|
|
<meta name="twitter:description" content="Javascript Animation Engine">
|
|
<meta name="twitter:image" content="https://animejs.com/documentation/assets/img/icons/twitter.png">
|
|
<link rel="apple-touch-icon-precomposed" href="../assets/img/social-media-image.png">
|
|
<link rel="icon" type="image/png" href="../assets/img/favicon.png" >
|
|
<link href="../assets/css/animejs.css" rel="stylesheet">
|
|
<style>
|
|
|
|
:root {
|
|
font-size: 24px;
|
|
}
|
|
|
|
body {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.feature-section {
|
|
position: relative;
|
|
display: flex;
|
|
}
|
|
|
|
.feature-section-center {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 64px;
|
|
line-height: 56px;
|
|
letter-spacing: 0;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.section-title span {
|
|
display: inline-block;
|
|
}
|
|
|
|
.section-description {
|
|
font-size: 24px;
|
|
line-height: 32px;
|
|
letter-spacing: 0;
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.feature-anim {
|
|
position: relative;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
|
|
.time-control {
|
|
height: 12rem;
|
|
}
|
|
|
|
.time-control .ruller {
|
|
position: absolute;
|
|
top: 0;
|
|
display: flex;
|
|
width: calc(34rem + 4px);
|
|
padding: 0 1px;
|
|
}
|
|
|
|
.time-control .line {
|
|
width: 2px;
|
|
height: .5rem;
|
|
background-color: currentColor;
|
|
}
|
|
|
|
.time-control .line:nth-child(9n+2) {
|
|
height: 1rem;
|
|
}
|
|
|
|
.time-control .line:not(:last-child) {
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.time-cursor {
|
|
position: absolute;
|
|
z-index: 1;
|
|
top: -1.125rem;
|
|
left: calc(-1rem + 2px);
|
|
width: 2rem;
|
|
height: 2rem;
|
|
background-color: currentColor;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.time-cursor input {
|
|
position: relative;
|
|
z-index: 1;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
color: #FFF;
|
|
background-color: transparent;
|
|
border-radius: 50%;
|
|
text-align: center;
|
|
font-size: .5rem;
|
|
}
|
|
|
|
.time-cursor:before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: calc(1rem - 1px);
|
|
display: block;
|
|
width: 2px;
|
|
height: 12.125rem;
|
|
background-color: #F64E4D;
|
|
}
|
|
|
|
.timeline {
|
|
position: relative;
|
|
z-index: 1;
|
|
overflow: visible;
|
|
display: flex;
|
|
width: 100%;
|
|
height: 8px;
|
|
font-size: .75rem;
|
|
}
|
|
|
|
.animation {
|
|
position: relative;
|
|
overflow: visible;
|
|
display: flex;
|
|
}
|
|
|
|
.animation-band {
|
|
position: relative;
|
|
height: 8px;
|
|
margin: 0 1px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.animation-delay {
|
|
background-color: currentColor;
|
|
width: 48px;
|
|
}
|
|
.animation-change {
|
|
background-color: #F64E4D;
|
|
width: 156px;
|
|
}
|
|
.animation-end-delay {
|
|
border: 2px solid currentColor;
|
|
width: 48px;
|
|
}
|
|
|
|
.info {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 2px;
|
|
height: 2rem;
|
|
margin: 0 1px;
|
|
}
|
|
|
|
.info + .info { margin-left: -3px; }
|
|
|
|
.info-large { height: 4rem; }
|
|
|
|
.info-top {
|
|
bottom: 0;
|
|
justify-content: flex-start;
|
|
align-items: flex-end;
|
|
align-self: flex-end;
|
|
}
|
|
|
|
.info-bottom {
|
|
top: 0;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.info-bar {
|
|
position: absolute;
|
|
width: 2px;
|
|
height: 100%;
|
|
background-color: currentColor;
|
|
transform-origin: 50% 100%;
|
|
}
|
|
|
|
.info-top .info-bar { bottom: 0; }
|
|
.info-bottom .info-bar { top: 0; transform-origin: 50% 0%; }
|
|
.info-left .info-bar { left: 0; }
|
|
.info-right .info-bar { right: 0; }
|
|
|
|
.info-label {
|
|
white-space: nowrap;
|
|
height: 1rem;
|
|
padding-left: .5rem;
|
|
padding-right: .5rem;
|
|
line-height: 1rem;
|
|
}
|
|
|
|
.info-top .info-label {
|
|
margin: -4px 0 0;
|
|
}
|
|
|
|
.info-bottom .info-label {
|
|
margin: 0 0 -4px 0;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<section class="feature-section feature-section-center feature-control" style="margin-top: 1000px; margin-bottom: 1000px;">
|
|
|
|
<h1 class="section-title">
|
|
<span>Time</span> <span>control</span> <span>& callbacks</span>
|
|
</h1>
|
|
|
|
<p class="section-description">
|
|
Play, pause, control, reverse and trigger events in sync
|
|
</p>
|
|
|
|
<div class="time-control feature-anim">
|
|
<div class="ruller">
|
|
<div class="time-cursor color-red">
|
|
<input value="999" />
|
|
</div>
|
|
</div>
|
|
<div class="timeline">
|
|
<div class="info-loop-begin info info-large info-left info-bottom" data-delay="0">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">loop begin</div>
|
|
<div class="info-label">animation begin</div>
|
|
</div>
|
|
<div class="animation">
|
|
<div class="animation-band animation-delay"></div>
|
|
<div class="info-change-begin info info-left info-bottom" data-delay="100">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change begin</div>
|
|
</div>
|
|
<div class="animation-band animation-change"></div>
|
|
<div class="info-change-complete info info-right info-top" data-delay="400">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change complete</div>
|
|
</div>
|
|
<div class="animation-band animation-end-delay"></div>
|
|
</div>
|
|
<div class="info-loop-complete info info-large info-right info-top" data-delay="500">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">loop complete</div>
|
|
</div>
|
|
<div class="info info-large info-left info-bottom" data-delay="500">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">loop begin</div>
|
|
<div class="info-label">reverse</div>
|
|
</div>
|
|
<div class="animation">
|
|
<div class="animation-band animation-delay"></div>
|
|
<div class="info-change-begin info info-left info-bottom" data-delay="600">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change begin</div>
|
|
</div>
|
|
<div class="animation-band animation-change"></div>
|
|
<div class="info-change-complete info info-right info-top" data-delay="900">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change complete</div>
|
|
</div>
|
|
<div class="animation-band animation-end-delay"></div>
|
|
</div>
|
|
<div class="info-loop-complete info info-large info-right info-top" data-delay="1000">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">loop complete</div>
|
|
</div>
|
|
<div class="info info-large info-left info-bottom" data-delay="1000">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">loop begin</div>
|
|
<div class="info-label">reverse</div>
|
|
</div>
|
|
<div class="animation">
|
|
<div class="animation-band animation-delay"></div>
|
|
<div class="info-change-begin info info-left info-bottom" data-delay="1100">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change begin</div>
|
|
</div>
|
|
<div class="animation-band animation-change"></div>
|
|
<div class="info-change-complete info info-right info-top" data-delay="1400">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">change complete</div>
|
|
</div>
|
|
<div class="animation-band animation-end-delay"></div>
|
|
</div>
|
|
<div class="info-loop-complete info info-large info-right info-top" data-delay="1500">
|
|
<div class="info-bar"></div>
|
|
<div class="info-label">animation complete</div>
|
|
<div class="info-label">loop complete</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</body>
|
|
<script type="module">
|
|
|
|
import anime from '../../src/index.js';
|
|
|
|
const timeControlEl = document.querySelector('.time-control');
|
|
const rullerEl = document.querySelector('.ruller');
|
|
const timeEl = document.querySelector('.time-cursor input');
|
|
const infoEls = document.querySelectorAll('.info');
|
|
const fragment = document.createDocumentFragment();
|
|
const numberOfElements = 136;
|
|
|
|
function isElementInViewport(el, inCB, outCB) {
|
|
function handleIntersect(entries, observer) {
|
|
var entry = entries[0];
|
|
if (entry.isIntersecting) {
|
|
if (inCB) inCB(el, entry);
|
|
} else {
|
|
if (outCB) outCB(el, entry);
|
|
}
|
|
}
|
|
var observer = new IntersectionObserver(handleIntersect);
|
|
observer.observe(el);
|
|
}
|
|
|
|
for (let i = 0; i < numberOfElements; i++) {
|
|
const dotEl = document.createElement('div');
|
|
dotEl.classList.add('line');
|
|
fragment.appendChild(dotEl);
|
|
}
|
|
|
|
rullerEl.appendChild(fragment);
|
|
|
|
const timeControlAnimation = anime.timeline({
|
|
easing: 'linear',
|
|
autoplay: false
|
|
})
|
|
.add({
|
|
targets: '.time-cursor',
|
|
keyframes: [
|
|
{ translateY: ['-1rem', 0], duration: 100, easing: 'easeInQuad' },
|
|
{ translateX: 'calc(34rem - 6px)', duration: 1500 },
|
|
{ translateY: '-1rem', duration: 100, easing: 'easeOutQuad' }
|
|
],
|
|
duration: 1500,
|
|
update: function(anim) {
|
|
timeEl.value = anim.currentTime;
|
|
}
|
|
}, -100)
|
|
.add({
|
|
targets: '.ruller .line',
|
|
translateY: [ {value: '1rem'}, {value: '0rem'} ],
|
|
duration: 200,
|
|
delay: anime.stagger([0, 1500]),
|
|
easing: 'easeInOutSine'
|
|
}, -100)
|
|
|
|
for (var i = 0; i < infoEls.length; i++) {
|
|
var infoEl = infoEls[i];
|
|
var delay = parseFloat(anime.get(infoEl, 'data-delay'));
|
|
var direction = infoEl.classList.contains('info-bottom') ? -1 : 1;
|
|
timeControlAnimation
|
|
.add({
|
|
targets: infoEl.querySelector('.info-bar'),
|
|
scaleY: [0, 1],
|
|
duration: 250,
|
|
easing: 'easeOutCirc'
|
|
}, delay)
|
|
.add({
|
|
targets: infoEl.querySelectorAll('.info-label'),
|
|
opacity: [0, 1],
|
|
translateY: [direction * 10, 0],
|
|
duration: 150,
|
|
delay: anime.stagger(50, {start: 10, direction: direction > 0 ? 'reverse' : 'normal'}),
|
|
easing: 'easeOutSine'
|
|
}, delay)
|
|
}
|
|
|
|
var controlAnimationCanMove = false;
|
|
|
|
function moveControlAnimation() {
|
|
var rect = timeControlEl.getBoundingClientRect();
|
|
var top = rect.top;
|
|
var height = rect.height;
|
|
var windowHeight = window.innerHeight;
|
|
var scrolled = (top - windowHeight) * -1;
|
|
timeControlAnimation.seek((scrolled * 3) - 500);
|
|
if (controlAnimationCanMove) requestAnimationFrame(moveControlAnimation);
|
|
}
|
|
|
|
isElementInViewport(timeControlEl, function(el, entry) {
|
|
controlAnimationCanMove = true;
|
|
requestAnimationFrame(moveControlAnimation);
|
|
}, function(el, entry) {
|
|
controlAnimationCanMove = false;
|
|
});
|
|
|
|
</script>
|
|
</html>
|