Skip to content

Instantly share code, notes, and snippets.

@Owen-Delisle
Created July 10, 2019 16:41
Show Gist options
  • Save Owen-Delisle/d259a32008e52b022492c46cfd3b2582 to your computer and use it in GitHub Desktop.
Save Owen-Delisle/d259a32008e52b022492c46cfd3b2582 to your computer and use it in GitHub Desktop.
/**
* Class representing the CheckMark timeline.
*
* Made with Flow
*/
class CheckMark {
/**
* Returns the timeline's duration in milliseconds.
*/
static get duration() {
return 500;
}
static artboardAnimation() {
// Workaround for Safari bug
return document.querySelector("#CheckMark .flow-artboard").animate(
{
backgroundPosition: ["0px", "1px"]
},
{
delay: 0,
duration: 500
}
);
}
static outerAnimation0() {
return document.querySelector("#CheckMark .outer").animate(
{
top: ["29.87px", "29px"],
opacity: [1, 0.9]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static outerShapeAnimation0() {
return document.querySelector("#CheckMark .outer-svg").animate(
{
fill: ["#FD0178", "#0000FF"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static innerAnimation0() {
return document.querySelector("#CheckMark .inner").animate(
{
width: ["44px", "2px"],
height: ["44px", "2px"],
opacity: [1, 0]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static leftAnimation0() {
return document.querySelector("#CheckMark .left").animate(
{
left: ["30px", "20.22px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static leftShapeAnimation0() {
return document.querySelector("#CheckMark .left-svg").animate(
{
stroke: ["#FD0178", "#FFFFFF"],
strokeDashoffset: ["80px", "68px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static rightAnimation0() {
return document.getElementById("checkMarkStrokeRight").animate(
{
left: ["30px", "36.22px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static rightShapeAnimation0() {
return document.getElementById("checkMarkStrokeRightSVG").animate(
{
stroke: ["#FD0178", "#FFFFFF"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
/**
* Creates and returns all animations for this timeline.
*/
static createAllAnimations() {
return [
this.artboardAnimation(),
this.outerAnimation0(),
this.outerShapeAnimation0(),
this.innerAnimation0(),
this.leftAnimation0(),
this.leftShapeAnimation0(),
this.rightAnimation0(),
this.rightShapeAnimation0()
];
}
}
CheckMark.allShapes = [
document.querySelector("#CheckMark .outer-svg"),
document.querySelector("#CheckMark .inner-svg"),
document.querySelector("#CheckMark .left-svg"),
document.getElementById("checkMarkStrokeRightSVG")
];
Object.freeze(CheckMark);
<!-- Made with Flow -->
<!DOCTYPE html>
<html>
<head>
<title>CheckMark</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/CheckMark.css">
<link rel="stylesheet" href="css/driver.css">
</head>
<body>
<div class="flow-artboard" id="CheckMark">
<div class="flow-layer outer">
<svg class="outer-svg" preserveAspectRatio="none" viewBox="0 0 56 56">
<title>outer</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M28,56c15.464,0,28,-12.536,28,-28 0,-15.464,-12.536,-28,-28,-28 -15.464,0,-28,12.536,-28,28 0,15.464,12.536,28,28,28zM28,56">
</path>
</svg>
<div class="flow-border outer-border"></div>
</div>
<div class="flow-layer inner">
<svg class="inner-svg" preserveAspectRatio="none" viewBox="0 0 44 44">
<title>inner</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M22,44c-12.15,0,-22,-9.85,-22,-22 0,-12.15,9.85,-22,22,-22 12.15,0,22,9.85,22,22 0,12.15,-9.85,22,-22,22zM22,44">
</path>
</svg>
<div class="flow-border inner-border"></div>
</div>
<div class="flow-layer left">
<svg class="left-svg" preserveAspectRatio="none" viewBox="0 0 20 20">
<title>left</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M0,0c0,0,0,0,0,0 0,0,20,20,20,20 0,0,0,0,0,0">
</path>
</svg>
<div class="flow-border left-border"></div>
</div>
<div class="flow-layer right" id="checkMarkStrokeRight">
<svg class="right-svg" preserveAspectRatio="none" viewBox="0 0 20 20" id="checkMarkStrokeRightSVG">
<title>right</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M0,20c0,0,0,0,0,0 0,0,20,-20,20,-20 0,0,0,0,0,0">
</path>
</svg>
<div class="flow-border right-border"></div>
</div>
</div>
<script src="js/web-animations.min.js"></script>
<script src="js/CheckMark.js"></script>
<!-- Animation driver panel, remove if desired -->
<div class="playback">
<div class="slider">
<input type="range" min="0" max="1" step="0.001" value="0.0" id="slider-checkMark" />
</div>
<div class="controls">
<div class="playPauseLoop">
<div class="playPauseContainer loop">
<input type='checkbox' id="loopButton-checkMark"><label for="loopButton-checkMark" class="loopLabel" id="loopLabel" />
</div>
<div class="playPauseContainer">
<input type='checkbox' id="playPauseButton-checkMark"><label for="playPauseButton-checkMark" class="playPauseLabel" id="playPauseLabel" />
</div>
<div class="playPauseContainer">
<div class="time" id="timeLabel-checkMark">0 s</div>
</div>
</div>
</div>
</div>
<div id="timer"></div>
<script src="js/driver.js"></script>
<script type="text/javascript">
document.driver = new Driver(CheckMark, 'loopButton-checkMark', 'playPauseButton-checkMark', 'slider-checkMark', 'timeLabel-checkMark')
document.driver.pause()
document.driver.currentTime = 0
document.addEventListener('keyup', function (event) {
if (event.defaultPrevented) {
return;
}
var key = event.key || event.keyCode;
if (key === 'Space' || key === " " || key === 32) {
let button = document.driver.playPauseButton
button.checked = !button.checked
document.driver.togglePlayback()
}
});
</script>
<!-- Animation driver panel end -->
</body>
</html>
/**
* Class representing the CheckMark timeline.
*
* Made with Flow
*/
class CheckMark {
/**
* Returns the timeline's duration in milliseconds.
*/
static get duration() {
return 500;
}
static artboardAnimation() {
// Workaround for Safari bug
return document.querySelector("#CheckMark .flow-artboard").animate(
{
backgroundPosition: ["0px", "1px"]
},
{
delay: 0,
duration: 500
}
);
}
static outerAnimation0() {
return document.querySelector("#CheckMark .outer").animate(
{
top: ["29.87px", "29px"],
opacity: [1, 0.9]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static outerShapeAnimation0() {
return document.querySelector("#CheckMark .outer-svg").animate(
{
fill: ["#FD0178", "#0000FF"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static innerAnimation0() {
return document.querySelector("#CheckMark .inner").animate(
{
width: ["44px", "2px"],
height: ["44px", "2px"],
opacity: [1, 0]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static leftAnimation0() {
return document.querySelector("#CheckMark .left").animate(
{
left: ["30px", "20.22px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static leftShapeAnimation0() {
return document.querySelector("#CheckMark .left-svg").animate(
{
stroke: ["#FD0178", "#FFFFFF"],
strokeDashoffset: ["80px", "68px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static rightAnimation0() {
return document.getElementById("checkMarkStrokeRight").animate(
{
left: ["30px", "36.22px"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
static rightShapeAnimation0() {
return document.getElementById("checkMarkStrokeRightSVG").animate(
{
stroke: ["#FD0178", "#FFFFFF"]
},
{
delay: 0,
duration: 500,
easing: "ease-in-out",
composite: "add",
fill: "forwards"
}
);
}
/**
* Creates and returns all animations for this timeline.
*/
static createAllAnimations() {
return [
this.artboardAnimation(),
this.outerAnimation0(),
this.outerShapeAnimation0(),
this.innerAnimation0(),
this.leftAnimation0(),
this.leftShapeAnimation0(),
this.rightAnimation0(),
this.rightShapeAnimation0()
];
}
}
CheckMark.allShapes = [
document.querySelector("#CheckMark .outer-svg"),
document.querySelector("#CheckMark .inner-svg"),
document.querySelector("#CheckMark .left-svg"),
document.getElementById("checkMarkStrokeRightSVG")
];
Object.freeze(CheckMark);
/**
* Class used for driving animations.
*/
class Driver {
constructor(timeline, loopID, playPauseID, sliderID, timerID) {
this.timeline = timeline
// Driver controls
this.slider = document.getElementById(sliderID)
this.animationTime = document.getElementById(timerID)
this.playPauseButton = document.getElementById(playPauseID)
this.loopButton = document.getElementById(loopID)
this.interval = setInterval(() => { this.updateSliderIfAnimating() }, 100/3)
this.createControlFunctions()
}
get timeline() {
return this._timeline
}
set timeline(timeline) {
if (this._timeline != null) {
this.pause()
}
this._timeline = timeline
this.timingAnimation = document.getElementById('timer').animate({}, timeline.duration)
this.timingAnimation.currentTime = 0
this.timingAnimation.pause()
this.animations = timeline.createAllAnimations()
this.shouldPlay = true
this.pause()
}
get duration() {
return this.timeline.duration
}
get currentTime() {
return this.timingAnimation.currentTime
}
set currentTime(time) {
for (const animation of this.animations) {
animation.currentTime = time
}
for (const shape of this.timeline.allShapes) {
shape.setCurrentTime(time / 1000)
}
this.timingAnimation.currentTime = time
}
updateSliderIfAnimating() {
if (this.timingAnimation.playState != "paused") {
this.sliderValue = this.currentTime / this.timeline.duration
this.setAnimationTimeLabels(this.currentTime)
}
}
play() {
this.interval = setInterval(() => { this.updateSliderIfAnimating() }, 100/3)
this.timingAnimation.play()
for (const animation of this.animations) {
animation.play()
}
for (const shape of this.timeline.allShapes) {
var t = shape.getCurrentTime() % this.timeline.duration
shape.setCurrentTime(t)
shape.unpauseAnimations()
}
}
isPlaying() {
if (this.timingAnimation == null) {
return false
}
return (this.timingAnimation.playState == "running")
}
pause() {
clearInterval(this.interval)
this.timingAnimation.pause()
for (const animation of this.animations) {
animation.pause()
}
for (const shape of this.timeline.allShapes) {
shape.pauseAnimations()
}
}
stop() {
this.playPauseButton.checked = false
this.shouldPlay = false
this.pause()
let d = this.duration
this.setAnimationTimeLabels(d)
this.sliderValue = d / this.timeline.duration - 0.001
}
togglePlayback() {
if (this.playPauseButton.checked) {
this.shouldPlay = true
if (this.timingAnimation.playState == "finished" ||
this.currentTime == this.timeline.duration) {
this.currentTime = 0
}
this.play()
} else {
this.shouldPlay = false
this.pause()
}
}
//------------------
// interface updates
//------------------
createControlFunctions() {
this.playPauseButton.onchange = () => {
this.togglePlayback()
}
this.slider.onchange = () => {
if (this.shouldPlay) {
this.play()
}
}
this.timingAnimation.onfinish = () => {
if (this.loopButton.checked) {
this.currentTime = 0
} else {
this.stop()
}
}
this.slider.oninput = () => {
this.pause()
var newTime = this.slider.value * this.duration
this.setAnimationTimeLabels(newTime)
var newTime = Math.min(newTime, this.duration - 1)
this.currentTime = newTime
}
this.slider.onchange = () => {
if (this.shouldPlay == true) {
this.play()
}
}
}
setAnimationTimeLabels(value) {
this.animationTime.innerHTML = Driver.convertTimeToString(value)
}
get sliderValue() {
return this.slider.value
}
set sliderValue(value) {
this.slider.value = value
}
//---------------
// helper methods
//---------------
static convertTimeToString(milliseconds) {
var truncatedMilliseconds = Math.floor(Math.floor(milliseconds % 1000, 0) / 10, 2)
var seconds = Math.floor(milliseconds / 1000, 0)
var timeString = seconds+""
if (truncatedMilliseconds != 0) {
timeString += "."
if (truncatedMilliseconds < 10) {
timeString += "0"
}
timeString += truncatedMilliseconds
}
return timeString+" s"
}
}
/**
* Class used for driving animations.
*/
class Driver {
constructor(timeline, loopID, playPauseID, sliderID, timerID) {
this.timeline = timeline
// Driver controls
this.slider = document.getElementById(sliderID)
this.animationTime = document.getElementById(timerID)
this.playPauseButton = document.getElementById(playPauseID)
this.loopButton = document.getElementById(loopID)
this.interval = setInterval(() => { this.updateSliderIfAnimating() }, 100/3)
this.createControlFunctions()
}
get timeline() {
return this._timeline
}
set timeline(timeline) {
if (this._timeline != null) {
this.pause()
}
this._timeline = timeline
this.timingAnimation = document.getElementById('timer').animate({}, timeline.duration)
this.timingAnimation.currentTime = 0
this.timingAnimation.pause()
this.animations = timeline.createAllAnimations()
this.shouldPlay = true
this.pause()
}
get duration() {
return this.timeline.duration
}
get currentTime() {
return this.timingAnimation.currentTime
}
set currentTime(time) {
for (const animation of this.animations) {
animation.currentTime = time
}
for (const shape of this.timeline.allShapes) {
shape.setCurrentTime(time / 1000)
}
this.timingAnimation.currentTime = time
}
updateSliderIfAnimating() {
if (this.timingAnimation.playState != "paused") {
this.sliderValue = this.currentTime / this.timeline.duration
this.setAnimationTimeLabels(this.currentTime)
}
}
play() {
this.interval = setInterval(() => { this.updateSliderIfAnimating() }, 100/3)
this.timingAnimation.play()
for (const animation of this.animations) {
animation.play()
}
for (const shape of this.timeline.allShapes) {
var t = shape.getCurrentTime() % this.timeline.duration
shape.setCurrentTime(t)
shape.unpauseAnimations()
}
}
isPlaying() {
if (this.timingAnimation == null) {
return false
}
return (this.timingAnimation.playState == "running")
}
pause() {
clearInterval(this.interval)
this.timingAnimation.pause()
for (const animation of this.animations) {
animation.pause()
}
for (const shape of this.timeline.allShapes) {
shape.pauseAnimations()
}
}
stop() {
this.playPauseButton.checked = false
this.shouldPlay = false
this.pause()
let d = this.duration
this.setAnimationTimeLabels(d)
this.sliderValue = d / this.timeline.duration - 0.001
}
togglePlayback() {
if (this.playPauseButton.checked) {
this.shouldPlay = true
if (this.timingAnimation.playState == "finished" ||
this.currentTime == this.timeline.duration) {
this.currentTime = 0
}
this.play()
} else {
this.shouldPlay = false
this.pause()
}
}
//------------------
// interface updates
//------------------
createControlFunctions() {
this.playPauseButton.onchange = () => {
this.togglePlayback()
}
this.slider.onchange = () => {
if (this.shouldPlay) {
this.play()
}
}
this.timingAnimation.onfinish = () => {
if (this.loopButton.checked) {
this.currentTime = 0
} else {
this.stop()
}
}
this.slider.oninput = () => {
this.pause()
var newTime = this.slider.value * this.duration
this.setAnimationTimeLabels(newTime)
var newTime = Math.min(newTime, this.duration - 1)
this.currentTime = newTime
}
this.slider.onchange = () => {
if (this.shouldPlay == true) {
this.play()
}
}
}
setAnimationTimeLabels(value) {
this.animationTime.innerHTML = Driver.convertTimeToString(value)
}
get sliderValue() {
return this.slider.value
}
set sliderValue(value) {
this.slider.value = value
}
//---------------
// helper methods
//---------------
static convertTimeToString(milliseconds) {
var truncatedMilliseconds = Math.floor(Math.floor(milliseconds % 1000, 0) / 10, 2)
var seconds = Math.floor(milliseconds / 1000, 0)
var timeString = seconds+""
if (truncatedMilliseconds != 0) {
timeString += "."
if (truncatedMilliseconds < 10) {
timeString += "0"
}
timeString += truncatedMilliseconds
}
return timeString+" s"
}
}
<!-- Made with Flow -->
<!DOCTYPE html>
<html>
<head>
<title>CheckMark</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/CheckMark.css">
<link rel="stylesheet" href="css/driver.css">
</head>
<body>
<div class="flow-artboard" id="CheckMark">
<div class="flow-layer outer">
<svg class="outer-svg" preserveAspectRatio="none" viewBox="0 0 56 56">
<title>outer</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M28,56c15.464,0,28,-12.536,28,-28 0,-15.464,-12.536,-28,-28,-28 -15.464,0,-28,12.536,-28,28 0,15.464,12.536,28,28,28zM28,56">
</path>
</svg>
<div class="flow-border outer-border"></div>
</div>
<div class="flow-layer inner">
<svg class="inner-svg" preserveAspectRatio="none" viewBox="0 0 44 44">
<title>inner</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M22,44c-12.15,0,-22,-9.85,-22,-22 0,-12.15,9.85,-22,22,-22 12.15,0,22,9.85,22,22 0,12.15,-9.85,22,-22,22zM22,44">
</path>
</svg>
<div class="flow-border inner-border"></div>
</div>
<div class="flow-layer left">
<svg class="left-svg" preserveAspectRatio="none" viewBox="0 0 20 20">
<title>left</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M0,0c0,0,0,0,0,0 0,0,20,20,20,20 0,0,0,0,0,0">
</path>
</svg>
<div class="flow-border left-border"></div>
</div>
<div class="flow-layer right" id="checkMarkStrokeRight">
<svg class="right-svg" preserveAspectRatio="none" viewBox="0 0 20 20" id="checkMarkStrokeRightSVG">
<title>right</title>
<desc>Made with Flow.</desc>
<path vector-effect="non-scaling-stroke" d="M0,20c0,0,0,0,0,0 0,0,20,-20,20,-20 0,0,0,0,0,0">
</path>
</svg>
<div class="flow-border right-border"></div>
</div>
</div>
<script src="js/web-animations.min.js"></script>
<script src="js/CheckMark.js"></script>
<!-- Animation driver panel, remove if desired -->
<div class="playback">
<div class="slider">
<input type="range" min="0" max="1" step="0.001" value="0.0" id="slider-checkMark" />
</div>
<div class="controls">
<div class="playPauseLoop">
<div class="playPauseContainer loop">
<input type='checkbox' id="loopButton-checkMark"><label for="loopButton-checkMark" class="loopLabel" id="loopLabel" />
</div>
<div class="playPauseContainer">
<input type='checkbox' id="playPauseButton-checkMark"><label for="playPauseButton-checkMark" class="playPauseLabel" id="playPauseLabel" />
</div>
<div class="playPauseContainer">
<div class="time" id="timeLabel-checkMark">0 s</div>
</div>
</div>
</div>
</div>
<div id="timer"></div>
<script src="js/driver.js"></script>
<script type="text/javascript">
document.driver = new Driver(CheckMark, 'loopButton-checkMark', 'playPauseButton-checkMark', 'slider-checkMark', 'timeLabel-checkMark')
document.driver.pause()
document.driver.currentTime = 0
document.addEventListener('keyup', function (event) {
if (event.defaultPrevented) {
return;
}
var key = event.key || event.keyCode;
if (key === 'Space' || key === " " || key === 32) {
let button = document.driver.playPauseButton
button.checked = !button.checked
document.driver.togglePlayback()
}
});
</script>
<!-- Animation driver panel end -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment