Skip to content

Instantly share code, notes, and snippets.

@zzzev
Created March 15, 2019 01:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zzzev/49b0d12164fb2d5d98bd30f8ca53cf67 to your computer and use it in GitHub Desktop.
Save zzzev/49b0d12164fb2d5d98bd30f8ca53cf67 to your computer and use it in GitHub Desktop.
// URL: https://observablehq.com/@zzzev/slit-scan-effect
// Title: Slit Scan Effect
// Author: Zev Youra (@zzzev)
// Version: 131
// Runtime version: 1
const m0 = {
id: "c56936685e3167a3@131",
variables: [
{
inputs: ["md"],
value: (function(md){return(
md`# Slit Scan Effect
An interpretation of the [slit-scan effect](https://en.wikipedia.org/wiki/Slit-scan_photography) as popularized in 2001: A Space Odyssey, and more recently [Netflix's recently updated load animation](https://gfycat.com/SlimImpishArkshell).`
)})
},
{
name: "canvas",
inputs: ["DOM","width","height","d3","numRects","center"],
value: (function*(DOM,width,height,d3,numRects,center)
{
const c = DOM.context2d(width, height)
function drawR(x = 0, w = 10, fill = "rgba(0,0,0,0.1)") {
fill = d3.color(fill);
const edge = d3.color(fill);
fill.opacity = 0.5;
edge.opacity = 0;
const g = c.createLinearGradient(x, 0, x + w, 0);
g.addColorStop(0, edge);
g.addColorStop(0.25, fill);
g.addColorStop(0.75, fill);
g.addColorStop(1, edge);
c.fillStyle = g;
c.fillRect(x, 0, w, height);
}
const xRand = d3.randomNormal(0, 10);
const wRand = d3.randomNormal(100, 100);
const fRand = d3.randomNormal(0, 1);
const vRand = d3.randomNormal(0, 1);
let rects = [];
while (true) {
c.clearRect(0, 0, width, height);
if (rects.length < numRects) {
const w = 1;
const tw = wRand();
const x = center - (w / 2) + xRand();
const f = d3.color(d3.interpolateRainbow(fRand()));
const v = vRand();
rects.push({x, w, tw, f, v})
}
rects.forEach((r) => {
const {x, w, tw, f, v} = r;
// const delta = center - (x + width / 2);
if (r.w < r.tw) {
r.w += 1;
r.x -= 0.5;
}
r.x = x + v;
r.v *= 1.01;
drawR(x, w, f);
})
rects = rects.filter(({x, w}) => x < width && x + w > 0);
yield c.canvas;
}
}
)
},
{
name: "viewof numRects",
inputs: ["slider"],
value: (function(slider){return(
slider({min: 1, max: 400, step: 1, title: "Max Bars"})
)})
},
{
name: "numRects",
inputs: ["Generators","viewof numRects"],
value: (G, _) => G.input(_)
},
{
name: "center",
inputs: ["width"],
value: (function(width){return(
width / 2
)})
},
{
name: "height",
inputs: ["width"],
value: (function(width){return(
width * 9 / 16
)})
},
{
name: "d3",
inputs: ["require"],
value: (function(require){return(
require("d3@5")
)})
},
{
from: "@jashkenas/inputs",
name: "slider",
remote: "slider"
}
]
};
const m1 = {
id: "@jashkenas/inputs",
variables: [
{
name: "slider",
inputs: ["input"],
value: (function(input){return(
function slider(config = {}) {
let {value, min = 0, max = 1, step = "any", precision = 2, title, description, getValue, format, display, submit} = config;
if (typeof config == "number") value = config;
if (value == null) value = (max + min) / 2;
precision = Math.pow(10, precision);
if (!getValue) getValue = input => Math.round(input.valueAsNumber * precision) / precision;
return input({
type: "range", title, description, submit, format, display,
attributes: {min, max, step, value},
getValue
});
}
)})
},
{
name: "input",
inputs: ["html","d3format"],
value: (function(html,d3format){return(
function input(config) {
let {
form,
type = "text",
attributes = {},
action,
getValue,
title,
description,
format,
display,
submit,
options
} = config;
if (!form)
form = html`<form>
<input name=input type=${type} />
</form>`;
const input = form.input;
Object.keys(attributes).forEach(key => {
const val = attributes[key];
if (val != null) input.setAttribute(key, val);
});
if (submit)
form.append(
html`<input name=submit type=submit style="margin: 0 0.75em" value="${
typeof submit == "string" ? submit : "Submit"
}" />`
);
form.append(
html`<output name=output style="font: 14px Menlo, Consolas, monospace; margin-left: 0.5em;"></output>`
);
if (title)
form.prepend(
html`<div style="font: 700 0.9rem sans-serif;">${title}</div>`
);
if (description)
form.append(
html`<div style="font-size: 0.85rem; font-style: italic;">${description}</div>`
);
if (format) format = d3format.format(format);
if (action) {
action(form);
} else {
const verb = submit
? "onsubmit"
: type == "button"
? "onclick"
: type == "checkbox" || type == "radio"
? "onchange"
: "oninput";
form[verb] = e => {
e && e.preventDefault();
const value = getValue ? getValue(input) : input.value;
if (form.output)
form.output.value = display
? display(value)
: format
? format(value)
: value;
form.value = value;
if (verb !== "oninput")
form.dispatchEvent(new CustomEvent("input", { bubbles: true }));
};
if (verb !== "oninput")
input.oninput = e => e && e.stopPropagation() && e.preventDefault();
if (verb !== "onsubmit") form.onsubmit = e => e && e.preventDefault();
form[verb]();
}
return form;
}
)})
},
{
name: "d3format",
inputs: ["require"],
value: (function(require){return(
require("d3-format@1")
)})
}
]
};
const notebook = {
id: "c56936685e3167a3@131",
modules: [m0,m1]
};
export default notebook;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment