Skip to content

Instantly share code, notes, and snippets.

Created July 14, 2017 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anonymous/c94a4a0e4f2a2104ce90b921e84d0080 to your computer and use it in GitHub Desktop.
Save anonymous/c94a4a0e4f2a2104ce90b921e84d0080 to your computer and use it in GitHub Desktop.
Eleven — Stranger Things Cinemagraph

Eleven — Stranger Things Cinemagraph

Requires a browser that can handle CSS filter (or -webkit-filter).

A Pen by Matt Popovich on CodePen.

License.

<canvas id="world"></canvas>
<canvas class="grain"></canvas>
<div class="hawkins">
<div class="el">
<div class="part eyelids"></div>
<div class="part arm"></div>
<div class="part hand"></div>
<div class="part eye-problems"></div>
<div class="part blood"></div>
</div>
</div>
// Canvas grain by Renārs Vilnis:
// https://codepen.io/renarsvilnis/pen/YWKRvE
// Falling confetti by Linmiao Xu
// https://codepen.io/linrock/pen/Amdhr?editors=1010
'use strict';
console.clear();
class Grain {
constructor (el) {
/**
* Options
* Increase the pattern size if visible pattern
*/
this.patternSize = 200;
this.patternScaleX = 1000;
this.patternScaleY = 1000;
this.patternRefreshInterval = 5;
this.patternAlpha = 18;
/**
* Create canvas
*/
this.canvas = el;
this.ctx = this.canvas.getContext('2d');
this.ctx.scale(this.patternScaleX, this.patternScaleY);
/**
* Create a canvas that will be used to generate grain and used as a
* pattern on the main canvas.
*/
this.patternCanvas = document.createElement('canvas');
this.patternCanvas.width = this.patternSize;
this.patternCanvas.height = this.patternSize;
this.patternCtx = this.patternCanvas.getContext('2d');
this.patternData = this.patternCtx.createImageData(this.patternSize, this.patternSize);
this.patternPixelDataLength = this.patternSize * this.patternSize * 4; // rgba = 4
/**
* Prebind prototype function, so later its easier to user
*/
this.resize = this.resize.bind(this);
this.loop = this.loop.bind(this);
this.frame = 0;
window.addEventListener('resize', this.resize);
this.resize();
window.requestAnimationFrame(this.loop);
}
resize () {
this.canvas.width = window.innerWidth * devicePixelRatio;
this.canvas.height = window.innerHeight * devicePixelRatio;
}
update () {
const {patternPixelDataLength, patternData, patternAlpha, patternCtx} = this;
// put a random shade of gray into every pixel of the pattern
for (let i = 0; i < patternPixelDataLength; i += 4) {
// const value = (Math.random() * 255) | 0;
const value = Math.random() * 255;
patternData.data[i] = value;
patternData.data[i + 1] = value;
patternData.data[i + 2] = value;
patternData.data[i + 3] = patternAlpha;
}
patternCtx.putImageData(patternData, 0, 0);
}
draw () {
const {ctx, patternCanvas, canvas, viewHeight} = this;
const {width, height} = canvas;
// clear canvas
ctx.clearRect(0, 0, width, height);
// fill the canvas using the pattern
ctx.fillStyle = ctx.createPattern(patternCanvas, 'repeat');
ctx.fillRect(0, 0, width, height);
}
loop () {
// only update grain every n frames
const shouldDraw = ++this.frame % this.patternRefreshInterval === 0;
if (shouldDraw) {
this.update();
this.draw();
}
window.requestAnimationFrame(this.loop);
}
}
/**
* Initiate Grain
*/
const el = document.querySelector('.grain');
const grain = new Grain(el);
(function() {
var COLORS, Confetti, NUM_CONFETTI, PI_2, canvas, confetti, context, drawCircle, i, range, resizeWindow, xpos;
NUM_CONFETTI = 350;
COLORS = [[85, 71, 106], [174, 61, 99], [219, 56, 83], [244, 92, 68], [248, 182, 70]];
PI_2 = 2 * Math.PI;
canvas = document.getElementById("world");
context = canvas.getContext("2d");
window.w = 0;
window.h = 0;
resizeWindow = function() {
window.w = canvas.width = window.innerWidth;
return window.h = canvas.height = window.innerHeight;
};
window.addEventListener('resize', resizeWindow, false);
window.onload = function() {
return setTimeout(resizeWindow, 0);
};
range = function(a, b) {
return (b - a) * Math.random() + a;
};
drawCircle = function(x, y, r, style) {
context.beginPath();
context.arc(x, y, r, 0, PI_2, false);
context.fillStyle = style;
return context.fill();
};
xpos = 0.5;
document.onmousemove = function(e) {
return xpos = e.pageX / w;
};
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
return window.setTimeout(callback, 1000 / 60);
};
})();
Confetti = (function() {
function Confetti() {
this.style = COLORS[~~range(0, 5)];
this.rgb = "rgba(" + this.style[0] + "," + this.style[1] + "," + this.style[2];
this.r = ~~range(2, 6);
this.r2 = 2 * this.r;
this.replace();
}
Confetti.prototype.replace = function() {
this.opacity = 0;
this.dop = 0.03 * range(1, 4);
this.x = range(-this.r2, w - this.r2);
this.y = range(-20, h - this.r2);
this.xmax = w - this.r;
this.ymax = h - this.r;
this.vx = range(0, 2) + 8 * xpos - 5;
return this.vy = 0.7 * this.r + range(-1, 1);
};
Confetti.prototype.draw = function() {
var ref;
this.x += this.vx;
this.y += this.vy;
this.opacity += this.dop;
if (this.opacity > 1) {
this.opacity = 1;
this.dop *= -1;
}
if (this.opacity < 0 || this.y > this.ymax) {
this.replace();
}
if (!((0 < (ref = this.x) && ref < this.xmax))) {
this.x = (this.x + this.xmax) % this.xmax;
}
return drawCircle(~~this.x, ~~this.y, this.r, this.rgb + "," + this.opacity + ")");
};
return Confetti;
})();
confetti = (function() {
var j, ref, results;
results = [];
for (i = j = 1, ref = NUM_CONFETTI; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
results.push(new Confetti);
}
return results;
})();
window.step = function() {
var c, j, len, results;
requestAnimationFrame(step);
context.clearRect(0, 0, w, h);
results = [];
for (j = 0, len = confetti.length; j < len; j++) {
c = confetti[j];
results.push(c.draw());
}
return results;
};
step();
}).call(this);
$anim-length: 10s;
html, body { height: 100%; }
body {
display: flex;
align-items: center;
justify-content: center;
background-color: black;
overflow: hidden;
}
#world {
position: absolute;
z-index: 7;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
filter: brightness(1000%);
-webkit-filter: brightness(1000%);
animation: upside-rain $anim-length linear infinite;
}
@keyframes upside-rain {
0% { opacity: 0; }
30% { opacity: 0; }
32% { opacity: 0.075; }
50% { opacity: 0.075; }
52% { opacity: 0; }
100% { opacity: 0; }
}
.grain {
position: absolute;
z-index: 9;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.hawkins {
display: flex;
align-items: center;
justify-content: center;
width: 700px;
height: 500px;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782962/posterback_vrnptz.png);
background-size: 650px;
background-repeat: no-repeat;
background-position: center;
animation: upside-down $anim-length linear infinite;
}
@keyframes upside-down {
0% { filter: none; -webkit-filter: none; }
30% { filter: none; -webkit-filter: none; }
31% { filter: brightness(400%); -webkit-filter: brightness(400%); }
32% { filter: brightness(400%); -webkit-filter: brightness(400%); }
33% {
filter: sepia(100%) hue-rotate(180deg) saturate(450%) contrast(115%) brightness(75%);
-webkit-filter: sepia(100%) hue-rotate(180deg) saturate(450%) contrast(115%) brightness(75%);
}
50% {
filter: sepia(100%) hue-rotate(180deg) saturate(450%) contrast(115%) brightness(75%);
-webkit-filter: sepia(100%) hue-rotate(180deg) saturate(450%) contrast(115%) brightness(75%);
}
51% { filter: brightness(400%); -webkit-filter: brightness(400%); }
52% { filter: brightness(400%); -webkit-filter: brightness(400%); }
53% { filter: none; -webkit-filter: none; }
100% { filter: none; -webkit-filter: none; }
}
.el {
position: relative;
margin-left: 65px;
width: 500px;
height: 500px;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782960/eleven_eyeca0.png);
background-size: 400px;
background-repeat: no-repeat;
background-position: center;
animation: el-shake 5s linear infinite;
}
@keyframes el-shake {
0% { transform: translate(0px, 0px); }
15% { transform: translate(1px, 0px); }
30% { transform: translate(0px, 1px); }
45% { transform: translate(-1px, 1px); }
60% { transform: translate(-1px, 0px); }
75% { transform: translate(0px, 0px); }
85% { transform: translate(0px, -1px); }
100% { transform: translate(0px, 0px); }
}
.part {
position: absolute;
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
}
.eyelids {
z-index: 5;
opacity: 0;
top: 163px;
left: 119px;
width: 117px;
height: 100px;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782959/eyelids_s12six.png);
animation: blink 6.5s ease-out infinite;
}
@keyframes blink {
0% { opacity: 0 }
20% { opacity: 0 }
21% { opacity: 0.8 }
21.5% { opacity: 0.8 }
23% { opacity: 0 }
60% { opacity: 0 }
61% { opacity: 0.95 }
61.5% { opacity: 0.95 }
63% { opacity: 0 }
70% { opacity: 0 }
71% { opacity: 0.85 }
71.5% { opacity: 0.85 }
73% { opacity: 0 }
100% { opacity: 0 }
}
.arm {
top: 232px;
left: 233px;
width: 150px;
height: 300px;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782959/arm_iwjvzv.png);
animation: arm-shake 5s ease-out infinite;
}
@keyframes arm-shake {
0% { transform: translate(0px, 1px); }
15% { transform: translate(1px, 1px); }
30% { transform: translate(0px, 1px); }
45% { transform: translate(0px, 1px); }
60% { transform: translate(1px, 0px); }
75% { transform: translate(1px, 0px); }
85% { transform: translate(1px, 1px); }
100% { transform: translate(0px, 1px); }
}
.hand {
top: 197px;
left: 181px;
width: 282px;
height: 300px;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782959/hand_kuy5dz.png);
animation: hand-shake 8s ease-out infinite;
}
@keyframes hand-shake {
0% { transform: translate(1px, -1px) scale(1.01); }
10% { transform: translate(-1px, -1px) scale(1.02); }
30% { transform: translate(1px, 1px) scale(1.05); }
35% { transform: translate(-1px, -1px) scale(1); }
45% { transform: translate(0px, 1px) scale(1.0175); }
60% { transform: translate(-1px, -1px) scale(1.01); }
85% { transform: translate(1px, 1px) scale(1.04); }
100% { transform: translate(1px, -1px) scale(1.01); }
}
.eye-problems {
top: 72px;
left: 107px;
width: 142px;
height: 300px;
opacity: 0.7;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782958/eye-problems_y1f6ne.png);
animation: side-effects $anim-length ease-out infinite;
}
@keyframes side-effects {
0% { opacity: 0; }
30% { opacity: 0; }
50% { opacity: .8; }
90% { opacity: 0; }
100% { opacity: 0; }
}
.blood {
top: 225px;
left: 180px;
width: 20px;
height: 100px;
opacity: 1;
background-image: url(https://res.cloudinary.com/dxscrtoxq/image/upload/v1468782958/blood_rnujxa.png);
animation: drip $anim-length ease-out infinite;
}
@keyframes drip {
0% { opacity: 0; }
30% { opacity: 0; }
50% { opacity: 1; }
80% { opacity: 1; }
100% { opacity: 0; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment