Skip to content

Instantly share code, notes, and snippets.

@enjalot
Forked from mbostock/.block
Created December 11, 2011 02:58
Show Gist options
  • Save enjalot/1457934 to your computer and use it in GitHub Desktop.
Save enjalot/1457934 to your computer and use it in GitHub Desktop.
d3.js sin waves
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<style type="text/css">
body {
background: #333;
}
rect {
stroke-width: 2.5px;
}
/*
.square:nth-child(2n + 1) rect {
}
*/
</style>
</head>
<body>
<svg></svg>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.6.0"></script>
<script type="text/javascript" src="jwerty.js"></script>
<script type="text/javascript">
var w = 960,
h = 500,
start = Date.now();
var rings = [ ];
var lines = [ ];
var n = 25;
var m = 70;
for (i in d3.range(n))
{
if(i < 3)
var speed = (i-3) * 1e-2;
else
var speed = (i-2) * 1e-2;
//var speed = 50e-2 / (i + 1)
//var speed = .001 * n / (.5*i+1)
var speed = .001 * i * 4;
var data = d3.range(m)
lines.push({
//radius: 65*i,
width: 5,
height: 15,
speed: speed,
//speed: speed_scale(i),
index: i,
data: data
})
}
var xscale = d3.scale.linear()
.domain([0,m])
.range([0, w])
var omega = -.22
function line_maker( data, speed )
{
var freq = Math.PI*.4 + 3 * omega * data.index // * 3000
var svgline = d3.svg.line()
.x(function(d,i)
{
return xscale(d);
})
.y(function(d,i)
{
var theta = freq * d/m * Math.PI * 4
//console.log("sin", Math.sin(theta), d)
var y = data.height * (Math.sin(theta + (n-data.index) * .1 * speed * .18 ));
//console.log ("y", y)
return y
})
.interpolate("basis")
return svgline(data.data);
}
var spacing = 26;
function lineEnter(d, i) {
//console.log("line enter", d)
d3.select(this).selectAll("path.path")
.data([d])
.enter()
.append("svg:path")
.attr("class", "path")
//.attr("transform", function(_, i) { return "translate(" + [0, h - spacing * d.index] + ")"; })
.attr("d", function(d,i) {
return line_maker( d, 0 )
}
)
.attr("stroke-width", function(e,i) { return e.width;})
.attr("stroke", "#fff")
.attr("fill", "none")
update_spacing()
}
var svg = d3.select("svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
//.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")scale(.6)");
var line = svg.selectAll("g.line")
.data(lines)
.enter().append("svg:g")
.attr("class", "line")
.each(lineEnter);
jwerty.key('a', function () {
omega -= .01;
console.log("omega", omega)
});
jwerty.key('d', function () {
omega += .01;
console.log("omega", omega)
});
var pause = false;
jwerty.key('p', function () {
pause = !pause;
});
var sm = .39
function update_spacing()
{
var th = spacing * n;
var hscale = d3.scale.linear()
.domain([0, n])
.range([0, h])
//console.log("th", th, hscale(99))
console.log("spacing", spacing)
d3.selectAll("g.line path")
.attr("transform", function(d, i) {
//console.log("h",h, spacing, d.index);
//return "translate(" + [0, th - spacing * d.index] + ")";
return "translate(" + [0, h/2 + th / 2 - spacing * d.index] + ")";
})
}
jwerty.key('↑', function () {
spacing += 1;
update_spacing();
});
jwerty.key('↓', function () {
spacing -= 1;
update_spacing();
});
//jwerty.key('↑', function () {
jwerty.key('←', function () {
sm -= .01;
console.log("speed multiplier", sm);
});
//jwerty.key('↓', function () {
jwerty.key('→', function () {
sm += .01;
console.log("speed multiplier", sm);
});
var color = d3.scale.linear()
.domain([-1, 1])
.interpolate(d3.interpolateRgb)
.range(['#fff', '#000'])
var opacity = d3.scale.linear()
.domain([0, n])
.range([1, .4])
b = 1;
d3.timer(function() {
if(pause) return false;
var elapsed = Date.now() - start
var damp = .3
rotate = function(d,i) {
var speed = sm * d.speed * elapsed * .1
return "rotate(" + speed + ")";
};
line = d3.selectAll("g.line path")
.attr("d", function(d,i) {
//var speed = a * d.speed * elapsed + .01 * d.index
var speed = sm * .08 * elapsed + d.index * 4
return line_maker( d, speed )
})
.attr("stroke-opacity", function(d,i) { return opacity(d.index);})
});
</script>
</body>
</html>
/*
* jwerty - Awesome handling of keyboard events
*
* jwerty is a JS lib which allows you to bind, fire and assert key combination
* strings against elements and events. It normalises the poor std api into
* something easy to use and clear.
*
* This code is licensed under the MIT
* For the full license see: http://keithamus.mit-license.org/
* For more information see: http://keithamus.github.com/jwerty
*
* @author Keith Cirkel ('keithamus') <[email protected]>
* @license http://keithamus.mit-license.org/
* @copyright Copyright © 2011, Keith Cirkel
*
*/
(function (global, exports) {
// Helper methods & vars:
var $d = global.document
, $ = (global.jQuery || global.Zepto || global.ender || $d)
, $$
, $b
, ke = 'keydown';
function realTypeOf(v, s) {
return (v === null) ? s === 'null'
: (v === undefined) ? s === 'undefined'
: (v.is && v instanceof $) ? s === 'element'
: Object.prototype.toString.call(v).toLowerCase().indexOf(s) > 7;
}
if ($ === $d) {
$$ = function (selector, context) {
return selector ? $.querySelector(selector, context || $) : $;
};
$b = function (e, fn) { e.addEventListener(ke, fn, false); };
$f = function (e, jwertyEv) {
var ret = document.createEvent('Event')
, i;
ret.initEvent(ke, true, true);
for (i in jwertyEv) ret[i] = jwertyEv[i];
return (e || $).dispatchEvent(ret);
}
} else {
$$ = function (selector, context, fn) { return $(selector || $d, context); };
$b = function (e, fn) { $(e).bind(ke + '.jwerty', fn); };
$f = function (e, ob) { $(e || $d).trigger($.Event(ke, ob)); };
}
// Private
var _modProps = { 16: 'shiftKey', 17: 'ctrlKey', 18: 'altKey', 91: 'metaKey' };
// Generate key mappings for common keys that are not printable.
var _keys = {
// MOD aka toggleable keys
mods: {
// Shift key, ⇧
'⇧': 16, shift: 16,
// CTRL key, on Mac: ⌃
'⌃': 17, ctrl: 17,
// ALT key, on Mac: ⌥ (Alt)
'⌥': 18, alt: 18, option: 18,
// META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
'⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
},
// Normal keys
keys: {
// Backspace key, on Mac: ⌫ (Backspace)
'⌫': 8, backspace: 8,
// Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
'⇥': 9, '⇆': 9, tab: 9,
// Return key, ↩
'↩': 13, 'return': 13, enter: 13, '⌅': 13,
// Pause/Break key
'pause': 19, 'pause-break': 19,
// Caps Lock key, ⇪
'⇪': 20, caps: 20, 'caps-lock': 20,
// Escape key, on Mac: ⎋, on Windows: Esc
'⎋': 27, escape: 27, esc: 27,
// Space key
space: 32,
// Page-Up key, or pgup, on Mac: ↖
'↖': 33, pgup: 33, 'page-up': 33,
// Page-Down key, or pgdown, on Mac: ↘
'↘': 34, pgdown: 34, 'page-down': 34,
// END key, on Mac: ⇟
'⇟': 35, end: 35,
// HOME key, on Mac: ⇞
'⇞': 36, home: 36,
// Insert key, or ins
ins: 45, insert: 45,
// Delete key, on Mac: ⌫ (Delete)
del: 45, 'delete': 45,
// Left Arrow Key, or ←
'←': 37, left: 37, 'arrow-left': 37,
// Up Arrow Key, or ↑
'↑': 38, up: 38, 'arrow-up': 38,
// Right Arrow Key, or →
'→': 39, right: 39, 'arrow-right': 39,
// Up Arrow Key, or ↓
'↓': 40, down: 40, 'arrow-down': 40,
// odities, printing characters that come out wrong:
// Num-Multiply, or *
'*': 106, star: 106, asterisk: 106, multiply: 106,
// Num-Plus or +
'+': 107, 'plus': 107,
// Num-Subtract, or -
'-': 109, subtract: 109,
//';': 186, //???
// = or equals
'=': 187, 'equals': 187,
// Comma, or ,
',': 188, comma: 188,
//'-': 189, //???
// Period, or ., or full-stop
'.': 190, period: 190, 'full-stop': 190,
// Slash, or /, or forward-slash
'/': 191, slash: 191, 'forward-slash': 191,
// Tick, or `, or back-quote
'`': 192, tick: 192, 'back-quote': 192,
// Open bracket, or [
'[': 219, 'open-bracket': 219,
// Back slash, or \
'\\': 220, 'back-slash': 220,
// Close backet, or ]
']': 221, 'close-bracket': 221,
// Apostraphe, or Quote, or '
'\'': 222, quote: 222, apostraphe: 222
}
};
// To minimise code bloat, add all of the NUMPAD 0-9 keys in a loop
i = 95, n = 0;
while(++i < 106) {
_keys.keys['num-' + n] = i;
++n;
}
// To minimise code bloat, add all of the top row 0-9 keys in a loop
i = 47, n = 0;
while(++i < 58) {
_keys.keys[n] = i;
++n;
}
// To minimise code bloat, add all of the F1-F25 keys in a loop
i = 111, n = 1;
while(++i < 136) {
_keys.keys['f' + n] = i;
++n;
}
// To minimise code bloat, add all of the letters of the alphabet in a loop
var i = 64;
while(++i < 91) {
_keys.keys[String.fromCharCode(i).toLowerCase()] = i;
}
function JwertyCode(jwertyCode) {
var i
, c
, n
, z
, keyCombo
, optionals
, jwertyCodeFragment
, rangeMatches
, rangeI;
// In-case we get called with an instance of ourselves, just return that.
if (jwertyCode instanceof JwertyCode) return jwertyCode;
// If jwertyCode isn't an array, cast it as a string and split into array.
if (!realTypeOf(jwertyCode, 'array')) {
jwertyCode = (String(jwertyCode)).replace(/\s/g, '').toLowerCase().
match(/(?:\+,|[^,])+/g);
}
// Loop through each key sequence in jwertyCode
for (i = 0, c = jwertyCode.length; i < c; ++i) {
// If the key combo at this part of the sequence isn't an array,
// cast as a string and split into an array.
if (!realTypeOf(jwertyCode[i], 'array')) {
jwertyCode[i] = String(jwertyCode[i])
.match(/(?:\+\/|[^\/])+/g);
}
// Parse the key optionals in this sequence
optionals = [], n = jwertyCode[i].length;
while (n--) {
// Begin creating the object for this key combo
var jwertyCodeFragment = jwertyCode[i][n];
keyCombo = {
jwertyCombo: String(jwertyCodeFragment),
shiftKey: false,
ctrlKey: false,
altKey: false,
metaKey: false
}
// If jwertyCodeFragment isn't an array then cast as a string
// and split it into one.
if (!realTypeOf(jwertyCodeFragment, 'array')) {
jwertyCodeFragment = String(jwertyCodeFragment).toLowerCase()
.match(/(?:(?:[^\+])+|\+\+|^\+$)/g);
}
z = jwertyCodeFragment.length;
while (z--) {
// Normalise matching errors
if (jwertyCodeFragment[z] === '++') jwertyCodeFragment[z] = '+';
// Inject either keyCode or ctrl/meta/shift/altKey into keyCombo
if (jwertyCodeFragment[z] in _keys.mods) {
keyCombo[_modProps[_keys.mods[jwertyCodeFragment[z]]]] = true;
} else if(jwertyCodeFragment[z] in _keys.keys) {
keyCombo.keyCode = _keys.keys[jwertyCodeFragment[z]];
} else {
rangeMatches = jwertyCodeFragment[z].match(/^\[([^-]+\-?[^-]*)-([^-]+\-?[^-]*)\]$/);
}
}
if (realTypeOf(keyCombo.keyCode, 'undefined')) {
// If we picked up a range match earlier...
if (rangeMatches && (rangeMatches[1] in _keys.keys) && (rangeMatches[2] in _keys.keys)) {
rangeMatches[2] = _keys.keys[rangeMatches[2]];
rangeMatches[1] = _keys.keys[rangeMatches[1]];
// Go from match 1 and capture all key-comobs up to match 2
for (rangeI = rangeMatches[1]; rangeI < rangeMatches[2]; ++rangeI) {
optionals.push({
altKey: keyCombo.altKey,
shiftKey: keyCombo.shiftKey,
metaKey: keyCombo.metaKey,
ctrlKey: keyCombo.ctrlKey,
keyCode: rangeI,
jwertyCombo: String(jwertyCodeFragment)
});
}
keyCombo.keyCode = rangeI;
// Inject either keyCode or ctrl/meta/shift/altKey into keyCombo
} else {
keyCombo.keyCode = 0;
}
}
optionals.push(keyCombo);
}
this[i] = optionals;
}
this.length = i;
return this;
}
var jwerty = exports.jwerty = {
/**
* jwerty.event
*
* `jwerty.event` will return a function, which expects the first
* argument to be a key event. When the key event matches `jwertyCode`,
* `callbackFunction` is fired. `jwerty.event` is used by `jwerty.key`
* to bind the function it returns. `jwerty.event` is useful for
* attaching to your own event listeners. It can be used as a decorator
* method to encapsulate functionality that you only want to fire after
* a specific key combo. If `callbackContext` is specified then it will
* be supplied as `callbackFunction`'s context - in other words, the
* keyword `this` will be set to `callbackContext` inside the
* `callbackFunction` function.
*
* @param {Mixed} jwertyCode can be an array, or string of key
* combinations, which includes optinals and or sequences
* @param {Function} callbackFucntion is a function (or boolean) which
* is fired when jwertyCode is matched. Return false to
* preventDefault()
* @param {Object} callbackContext (Optional) The context to call
* `callback` with (i.e this)
*
*/
event: function (jwertyCode, callbackFunction, callbackContext /*? this */) {
// Construct a function out of callbackFunction, if it is a boolean.
if (realTypeOf(callbackFunction, 'boolean')) {
var bool = callbackFunction;
callbackFunction = function () { return bool; }
}
jwertyCode = new JwertyCode(jwertyCode);
// Initialise in-scope vars.
var i = 0
, c = jwertyCode.length - 1
, returnValue
, jwertyCodeIs;
// This is the event listener function that gets returned...
return function (event) {
// if jwertyCodeIs returns truthy (string)...
if ((jwertyCodeIs = jwerty.is(jwertyCode, event, i))) {
// ... and this isn't the last key in the sequence,
// incriment the key in sequence to check.
if (i < c) {
++i;
return;
// ... and this is the last in the sequence (or the only
// one in sequence), then fire the callback
} else {
returnValue = callbackFunction.call(
callbackContext || this, event, jwertyCodeIs);
// If the callback returned false, then we should run
// preventDefault();
if (returnValue === false) event.preventDefault();
// Reset i for the next sequence to fire.
i = 0;
return;
}
}
// If the event didn't hit this time, we should reset i to 0,
// that is, unless this combo was the first in the sequence,
// in which case we should reset i to 1.
i = jwerty.is(jwertyCode, event) ? 1 : 0;
}
},
/**
* jwerty.is
*
* `jwerty.is` will return a boolean value, based on if `event` matches
* `jwertyCode`. `jwerty.is` is called by `jwerty.event` to check
* whether or not to fire the callback. `event` can be a DOM event, or
* a jQuery/Zepto/Ender manufactured event. The properties of
* `jwertyCode` (speficially ctrlKey, altKey, metaKey, shiftKey and
* keyCode) should match `jwertyCode`'s properties - if they do, then
* `jwerty.is` will return `true`. If they don't, `jwerty.is` will
* return `false`.
*
* @param {Mixed} jwertyCode can be an array, or string of key
* combinations, which includes optinals and or sequences
* @param {KeyboardEvent} event is the KeyboardEvent to assert against
* @param {Integer} i (Optional) checks the `i` key in jwertyCode
* sequence
*
*/
is: function (jwertyCode, event, i /*? 0*/) {
jwertyCode = new JwertyCode(jwertyCode);
// Default `i` to 0
i = i || 0;
// We are only interesting in `i` of jwertyCode;
jwertyCode = jwertyCode[i];
// jQuery stores the *real* event in `originalEvent`, which we use
// because it does annoything stuff to `metaKey`
event = event.originalEvent || event;
// We'll look at each optional in this jwertyCode sequence...
var key
, n = jwertyCode.length
, returnValue = false;
// Loop through each fragment of jwertyCode
while (n--) {
returnValue = jwertyCode[n].jwertyCombo;
// For each property in the jwertyCode object, compare to `event`
for (var p in jwertyCode[n]) {
// ...except for jwertyCode.jwertyCombo...
if (p !== 'jwertyCombo' && event[p] != jwertyCode[n][p]) returnValue = false;
}
// If this jwertyCode optional wasn't falsey, then we can return early.
if (returnValue !== false) return returnValue;
}
return returnValue;
},
/**
* jwerty.key
*
* `jwerty.key` will attach an event listener and fire
* `callbackFunction` when `jwertyCode` matches. The event listener is
* attached to `document`, meaning it will listen for any key events
* on the page (a global shortcut listener). If `callbackContext` is
* specified then it will be supplied as `callbackFunction`'s context
* - in other words, the keyword `this` will be set to
* `callbackContext` inside the `callbackFunction` function.
*
* @param {Mixed} jwertyCode can be an array, or string of key
* combinations, which includes optinals and or sequences
* @param {Function} callbackFunction is a function (or boolean) which
* is fired when jwertyCode is matched. Return false to
* preventDefault()
* @param {Object} callbackContext (Optional) The context to call
* `callback` with (i.e this)
* @param {Mixed} selector can be a string, jQuery/Zepto/Ender object,
* or an HTML*Element on which to bind the eventListener
* @param {Mixed} selectorContext can be a string, jQuery/Zepto/Ender
* object, or an HTML*Element on which to scope the selector
*
*/
key: function (jwertyCode, callbackFunction, callbackContext /*? this */, selector /*? document */, selectorContext /*? body */) {
// Because callbackContext is optional, we should check if the
// `callbackContext` is a string or element, and if it is, then the
// function was called without a context, and `callbackContext` is
// actually `selector`
var realSelector = realTypeOf(callbackContext, 'element') || realTypeOf(callbackContext, 'string') ? callbackContext : selector
// If `callbackContext` is undefined, or if we skipped it (and
// therefore it is `realSelector`), set context to `global`.
, realcallbackContext = realSelector === callbackContext ? global : callbackContext
// Finally if we did skip `callbackContext`, then shift
// `selectorContext` to the left (take it from `selector`)
, realSelectorContext = realSelector === callbackContext ? selector : selectorContext;
// If `realSelector` is already a jQuery/Zepto/Ender/DOM element,
// then just use it neat, otherwise find it in DOM using $$()
$b(realTypeOf(realSelector, 'element') ?
realSelector : $$(realSelector, realSelectorContext)
, jwerty.event(jwertyCode, callbackFunction, realcallbackContext));
},
/**
* jwerty.fire
*
* `jwerty.fire` will construct a keyup event to fire, based on
* `jwertyCode`. The event will be fired against `selector`.
* `selectorContext` is used to search for `selector` within
* `selectorContext`, similar to jQuery's
* `$('selector', 'context')`.
*
* @param {Mixed} jwertyCode can be an array, or string of key
* combinations, which includes optinals and or sequences
* @param {Mixed} selector can be a string, jQuery/Zepto/Ender object,
* or an HTML*Element on which to bind the eventListener
* @param {Mixed} selectorContext can be a string, jQuery/Zepto/Ender
* object, or an HTML*Element on which to scope the selector
*
*/
fire: function (jwertyCode, selector /*? document */, selectorContext /*? body */, i) {
jwertyCode = new JwertyCode(jwertyCode);
var realI = realTypeOf(selectorContext, 'number') ? selectorContext : i;
// If `realSelector` is already a jQuery/Zepto/Ender/DOM element,
// then just use it neat, otherwise find it in DOM using $$()
$f(realTypeOf(selector, 'element') ?
selector : $$(selector, selectorContext)
, jwertyCode[realI || 0][0]);
},
KEYS: _keys
};
}(this, (typeof module !== 'undefined' && module.exports ? module.exports : this)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment