GIFs
vs
Web Components
by
@GlenMaddern
Me?
Me & GIFs
Me & GIFs
Chapter One
The Idea
VastImg
ETOOMUCHPORN
GifCity
+
Tumblr
=
benschwarz.github.io/gifcity/?t=tumblrs,go,here
mrdiv
simpsonsgifs
skt-na-veia-sempre
benschwarz.github.io/gifcity/?t=tumblrs,go,here
DJGif
github.com/geelen/djgif
djgif.com (one day)
Two challenges:
- Change speed of GIFs
- Find beats from music
<img src="probably_cats.gif">
var img = new Image;
img.src = "definitely_cats.gif";
Now what?
✖
What is
an animation
but a
series of frames
?
What's in a GIF?
http://www.matthewflickinger.com/lab/ whatsinagif/bits_and_bytes.asp
header
frame frame frame
footer
header frame footer
header frame footer
header frame footer
exploder.js
Put the frames in the DOM
<div data-frame="0">
<img src="frame0.gif">
<img src="frame1.gif">
<img src="frame2.gif">
</div>
Show one at a time with CSS
[data-frame] img {
opacity: 0;
position: absolute;
}
[data-frame="0"] img:nth-child(1),
[data-frame="1"] img:nth-child(2),
[data-frame="2"] img:nth-child(3) {
opacity: 1;
}
Cycle through with JS
var animate = function() {
var currentFrame = calculateFrame();
framesElement.dataset.frame = currentFrame;
requestAnimationFrame(animate);
}
animate();
Chapter Two
The Component
Dreamcode
<img src='lulz.gif'>
<x-gif src='lulz.gif'>
<x-gif>
geelen.github.io/x-gif
geelen.github.io/x-gif
<x-gif
src=''
// Playback Modes
[speed='' || sync || bpm='']
// Playback options
stopped
fill
n-times=''
ping-pong
snap
>
Chapter Three
The Launch
Polymer
platform.js - 50kb
polymer.html - 1kb
polymer.js - 23kb
layout.html - 2kb
<x-gif>
x-gif.html - 1kb
x-gif.js - 11kb
x-gif.css - 3kb
7 requests, 91kb total
https://github.com/Polymer/vulcanize
https://github.com/Polymer/vulcanize
vulcanize -o dist/x-gif.vulcan.html --inline dist/x-gif.html
platform.js - 50kb
x-gif.vulcan.html - 34kb
2 requests, 84kb total
Polymer
- A framework in its own right
- Has opinions about structure, reuse
- Very easy to get started
- Seemed orthogonal to my purpose
What about Angular/Ember/React?
- Custom elements without Web Components
- Provide common functionality (Promises, XHR)
- Why not use them?
An idea!
- A framework-agnostic core, with adapters for each framework
- Consistent component API
- Bare minimum of dependencies for each framework
- One library, 4 entry points
Superficially, it should work
import XGifCore from './x-gif.core.js'
myModule.directive('xGif', XGifCore.Angular);
Ember.Component.extend(XGifCore.Ember);
React.createClass(XGifCore.React);
Polymer('x-gif', XGifCore.Polymer);
✖
Problems
- Frameworks aren't really compatible with one another
- All have different expectations/idioms for accomplishing things
An example - Playback
setFrame(fraction, repeatCount) {
var frameNr = (this.pingPong && repeatCount % 2 >= 1) ?
this.gif.frameAt(1 - fraction) :
this.gif.frameAt(fraction);
this.element.dataset['frame'] = frameNr;
}
React changes
setFrame(fraction, repeatCount) {
var frameNr = // SAME MATHS;
this.domUpdater.setFrame(frameNr);
}
class ShadowDomUpdater { /* ... */ }
class ReactStateUpdater {/* ... */ }
Shadow DOM
is
badass
:(
Chapter Four
The Solution
Chrome 36
Polymer
is not equal to
Web Components
Polymer
class XGif {
ready() { /* ... */ }
}
Polymer('x-gif', new XGif());
Web Components
class XGif extends HTMLElement {
createdCallback() { /* ... */ }
}
document.registerElement('x-gif', XGif);
Polymer
srcChanged() {
this.playback = new Playback(/* ... */)
}
speedChanged(oldVal, newVal) {
this.playback.speed = newVal;
}
Web Components
attributeChangedCallback(attribute, oldVal, newVal) {
if (attribute == 'src') {
this.controller.srcChanged(newVal);
} else if (attribute == 'speed') {
this.controller.speedChanged(parseFloat(newVal));
// ...
x-gif.html (Polymer)
<link rel='import' href='polymer.html'>
<polymer-element name='x-gif' attributes='src speed ... stopped'>
<template>
<link rel='stylesheet' href='x-gif.css' />
<div class='frames-wrapper'>
<div id='frames'></div>
</div>
</template>
</polymer-element>
<script src='x-gif.js'></script>
<!-- calls Polymer('x-gif', new XGif()) -->
x-gif.html (Web Component)
<template id='template'>
<link rel='stylesheet' href='x-gif.css' />
<div class='frames-wrapper'>
<div id='frames'></div>
</div>
</template>
<script src='x-gif.js'></script>
x-gif.js (Web Component)
class XGif extends HTMLElement {
createdCallback() {
this.shadow = this.xgif.createShadowRoot();
var owner = document.currentScript.ownerDocument;
var template = owner.querySelector('#template');
this.shadow.appendChild(template.content.cloneNode(true));
}
}
document.registerElement('x-gif', XGif);
Chrome 36+
<link rel='import' href='x-gif.html'>
<x-gif src='mrt_works_it.gif'></x-gif>
<x-gif>
x-gif.local.html - 1kb
x-gif.js - 11kb
x-gif.css - 3kb
Vulcanized
x-gif.html - 14kb
platform.js
... it's not just for Polymer
All browsers (IE9+)
<script>
if ('registerElement' in document
&& 'createShadowRoot' in HTMLElement.prototype
&& 'import' in document.createElement('link')
&& 'content' in document.createElement('template')) {
// We're using a browser with native WC support!
} else {
document.write("<script src='platform.js'></script>")
}
</script>
<link rel='import' href='x-gif.html'>
<x-gif src='mrt_works_it.gif'></x-gif>
platform.js - 50kb
x-gif.html - 14kb
What's so different?
Extend the
browser
not the
framework
Everything talks
HTML
Closing thoughts
- Really think about your HTML api
- Know what kind of component you're writing
The Web
is