toString hack
x=self+''
console.log(
x[8],x[9],x[6],' ',
x[3],x[1],x[2],' ',
x[10],x[4],x[8],' ',
x[5],x[1],x[11]
)
Hit the “Try it out” and open the console….
x=self+''
console.log(
x[8],x[9],x[6],' ',
x[3],x[1],x[2],' ',
x[10],x[4],x[8],' ',
x[5],x[1],x[11]
)
Hit the “Try it out” and open the console….
const canvas = document.createElement('canvas');
const c = canvas.getContext('2d');
document.body.appendChild(canvas);
function draw() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
c.fillStyle = 'gray';
c.fillRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = 'white';
c.lineWidth = 2;
c.beginPath();
c.moveTo(0, 0);
bezierSkin([10, 10, 210, 10, 10, 300, 210, 300], false);
bezierSkin([200, 10, 330, 10, 250, 300]);
bezierSkin(
Array(30)
.fill(0)
.map((a, b) => (b % 2 == 0) * 300 + Math.random() * 300)
);
c.stroke();
}
window.addEventListener('resize', draw);
draw();
// array of xy coords, closed boolean
function bezierSkin(bez, closed = true) {
const avg = calcAvgs(bez);
const leng = bez.length;
let i, n;
if (closed) {
c.moveTo(avg[0], avg[1]);
for (i = 2; i < leng; i += 2) {
n = i + 1;
c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
}
c.quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
} else {
c.moveTo(bez[0], bez[1]);
c.lineTo(avg[0], avg[1]);
for (i = 2; i < leng - 2; i += 2) {
n = i + 1;
c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
}
c.lineTo(bez[leng - 2], bez[leng - 1]);
}
}
// create anchor points by averaging the control points
function calcAvgs(p) {
const avg = [];
const leng = p.length;
let prev;
for (var i = 2; i < leng; i++) {
prev = i - 2;
avg.push((p[prev] + p[i]) / 2);
}
// close
avg.push((p[0] + p[leng - 2]) / 2);
avg.push((p[1] + p[leng - 1]) / 2);
return avg;
}
Being able to draw smooth lines that connect arbitrary points is something that I find myself needing very frequently. This is a port of an old old snippet of mine that does just that. By averaging control points of a quadratic bezier curve we ensure that our resulting Bezier curves are always smooth.
It would be very cool if html5 canvas implemented the Catmull Rom Spline but it unfortunately does not. The wonderful Raphael library used to have support for it.
const oCan = (
d = document,
b = d.body,
canvas = b.appendChild(document.createElement('canvas')),
c = canvas.getContext('2d'),
props = [],
o = {},
docs = {},
cmds = [],
L,
k,
du,
j,
draw,
id
) => {
;(onresize = () => {
canvas.width = innerWidth
canvas.height = innerHeight
if (draw) {
clearTimeout(id)
id = setTimeout(() => cmds.forEach(v => draw(v)), 500)
}
})()
Object.assign(b.style, { margin: 0, height: '100%' })
// longer way: console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(c)));
for (let i in c) props.push(i)
// make alphabetical since object keys have
// no order
props.sort().map(i => {
L = i.match(/[A-Z]/)
k = i[0]
if (L) k += L[0]
du = 0
if (o[k]) {
j = 0
while (o[k]) k += i[++j]
}
o[k] =
(typeof c[i])[0] == 'f'
? (...args) => c[i].apply(c, args)
: v => (c[i] = v)
docs[i] = k
})
console.log('docs:', JSON.stringify(docs, null, 2))
return (draw = (s, cmd, currFn, args = [], part, fn, num) => {
cmd = s.split(/\s+/)
cmds.push(s)
c.save()
for (let i = 0; i < cmd.length; i++) {
part = cmd[i]
fn = o[part]
if (fn && currFn != fn) {
currFn && currFn.apply(c, args)
currFn = fn
args = []
} else {
num = parseFloat(part)
args.push(!isNaN(num) ? num : part)
}
}
currFn && currFn.apply(c, args)
c.restore()
})
}
const c = oCan()
// `font` & text not suppoted
// make str a function so resize works?
c(`
fS #ccc
fR 0 0 400 ${innerHeight}
fS blue
fR 40 0 20 20
gCl difference
ro .25
fR 50 0 30 30
gCl source-over
fS rgba(200,100,9)
fR 100 100 40 40
`)
I’ve had this idea for a long time, never bothered doing it until now. I wrote it in a semi-golfed style for no reason… Anyway, this lets you write canvas code in a strange obfuscated syntax that looks like this:
c(`
fS #ccc
fR 0 0 400 ${innerHeight}
fS blue
fR 40 0 20 20
gCl difference
ro .25
fR 50 0 30 30
gCl source-over
fS rgba(200,100,9)
fR 100 100 40 40
`)
This snippet logs out some JSON that shows all the method aliases for the canvas context.
const spec = {
get(o, key) {
return o[key] != null ?
o[key] : o[key] = (...args) => {
const el = document.createElement(key);
args.forEach(arg => {
if (typeof arg === 'string') {
const span = document.createElement('span');
span.innerHTML = arg;
el.appendChild(span);
} else if (typeof arg === 'object') {
if (arg.tagName != null) {
el.appendChild(arg);
} else {
for (let i in arg) {
el.setAttribute(i, arg[i]);
}
}
}
});
return el;
}
},
set(o, key, v) {
o[key] = v;
}
}
const dom = new Proxy({}, spec);
document.body.appendChild(
dom.div(
dom.button('cool'),
dom.h2('some text', { style: 'font-style: italic' }),
dom.br(),
dom.input({ placeholder: 'zevan' })
)
);
const { div, input, label } = dom;
document.body.appendChild(
div(
label(
'Slider:',
{
for: 'slider',
style: 'padding:1em;display:block'
},
input({ id: 'slider', type: 'range' })
)
)
);
In this snippet a proxy is used that makes all html node tagNames
valid methods. Each method can take strings and HTMLElements
as arguments in any order to create a dom structure. This may look familiar to people who have looked a bit deeper into the inner workings of some of the popular UI libraries of the last decade.
const target = {}
let value = null;
Object.defineProperties(target, {
magic: {
get() {
console.log('- getter called::', value);
return value;
},
set(val) {
document.body.innerHTML += val + '<br>';
value = val;
}
}
});
target.magic = 'xyz';
target.magic = 'snippet';
target.magic = 'zone';
target.magic = '- last value';
console.log('getting', target.magic);
This snippet shows a way to add getters and setters to a specific key of an existing object. This is powerful for decorating configuration objects with special behaviors. This kind of thing can easily be created with a little more abstraction:
const image = view({
tag: 'img',
src: 'myImage.jpg',
x: '20%',
y: '20%',
size: '50%'
});
const prev = view({
tag: 'button',
x: () => image.x,
y: () => image.bottom
});
const next = view({
tag: 'button',
x: () => image.right - this.width,
y: () => image.bottom
});