<script src="http://fb.me/react-js-fiddle-integration.js"></script>
<script type="text/jsx">/** @jsx React.DOM */
<component extends="span" name="tickTockClock">
{function(template, props, state, update){
var now = new Date;
if (!state.now || +now - +state.now > 1000) {
state.now = now;
if (props.onchange) props.onchange(state.now);
}
state.alt = state.now.getSeconds() % 2;
var hour = state.now.getHours();
if (hour > 12) hour -= 12;
<template style={{background:"blue", color:"white"}}>
<span>{hour}</span>
<span id="sep" style={{ visibility:state.alt ? 'hidden':'visible' }}>:</span>
<span>{state.now.getMinutes()}</span>
<span id="sep" style={{ visibility:state.alt ? 'hidden':'visible' }}>:</span>
<span>{state.now.getSeconds()}</span>
</template>
setTimeout(update, 500);
}}
</component>
</script>
<script type="text/jsx">/** @jsx React.DOM */
<component extends="form" name="inputToy">
{function(template, props, state, redrawSoon){
if (!state.foo) state.foo = (props.max - props.min) * Math.random() + props.min;
var step = (props.max - props.min) / (props.steps || 100);
if (state.shouldRound) state.foo = Math.round(state.foo);
if (props.value) state.foo = props.value;
<Spring input={+state.foo} precision={1000}>{state.springState || (state.springState = {value:state.foo})}</Spring>;
if (state.springState.isDirty) redrawSoon();
<template style={props.style}>
<label>Round? <input type="checkbox" onChange={state.handlerFor('shouldRound')} /></label>
<input type="range" min={props.min} max={props.max} step={step} value={state.foo} onChange={state.handlerFor('foo')} />
<input type="number" min={props.min} max={props.max} step={step} value={state.foo} onChange={state.handlerFor('foo')} />
<input value={state.foo} readOnly size="4" />
<input value={state.springState.value} readOnly size="4" />
<Layer flow left={0} opacity={.25} w='100%' h="1.4em" scaleX={(state.springState.value - props.min) / props.max} origin='left center' />
</template>
}}
</component>
</script>
<script type="text/jsx">/** @jsx React.DOM */
<component extends="div" name="demo">
{function(template, props, state, update){
<template data-name="demo">
<tickTockClock onchange={state.setterFor('time')} />
<inputToy min="0" max="59" steps="1" value={state.time && state.time.getSeconds() || 0} />
<inputToy min="0" max="11" steps="33" />
<inputToy min="0" max="1" steps="8" />
<inputToy min="123" max="456" />
</template>
}}
</component>
</script>
<script type="text/jsx">/** @jsx React.DOM */
<Root><demo /></Root>
</script>
function Root(props, firstChild){
var parentNode = props && (props.parentNode || props.mountNode || (props.parentNodeId && document.getElementById(props.parentNodeId))) || document.body;
if (!parentNode) return setTimeout(Root.bind(this, props, firstChild), 1000/60);
var children = Array.prototype.slice.call(arguments, 1);
if (children.length > 1) firstChild = React.DOM[props && props.nodeName || 'div'](null, children);
React.renderComponent(
firstChild,
parentNode
);
}
function component(componentProps, render){
var root;
var superComponent, superComponentId = componentProps && componentProps["extends"];
superComponent = React.DOM[superComponentId] || window[superComponentId] || React.DOM.div;
function template(props){
var children = Array.prototype.slice.call(arguments, 1);
return root = superComponent(props, children);
}
window[componentProps.name] = React.createClass({
getInitialState: ComponentState.getInitialState,
_render:render,
render: function(){
this._render(template, this.props, this.state, this.state.updateEventually);
return root;
}
});
}
var requestAnimationFrame = (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){return setTimeout(callback, 1000/30);}).bind(window);
var cancelAnimationFrame = (window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || function(timerId){return clearTimeout(timerId);}).bind(window);
function ComponentState(component){
if (!(this instanceof ComponentState)) throw TypeError('ComponentState must be called with `new`');
var state = this;
state.update = component.replaceState.bind(component, state, undefined);
state.updateEventually = state.updateEventually.bind(state);
}
ComponentState.getInitialState = function(){
return new ComponentState(this);
}
ComponentState.prototype = {
constructor: ComponentState,
handlerFor: function(key){
if (this[key + 'Handler']) return this[key + 'Handler'];
return this[key + 'Handler'] = this.handler.bind(this, key);
},
handler: function(key, event){
var value = event.target.value;
if (event.target.type == 'checkbox'){
if (event.target.checked){
if (!value) value = true;
}
else value = null;
}
this.set(key, value);
},
setterFor: function(key){
if (this[key + 'Setter']) return this[key + 'Setter'];
return this[key + 'Setter'] = this.set.bind(this, key);
},
set: function(key, value){
this[key] = value;
this.updateEventually();
},
updateEventually: function(){
if (this._updateTimer) cancelAnimationFrame(this._updateTimer);
this._updateTimer = requestAnimationFrame(this.update);
},
update: function(callback){
throw Error('Update is an abstract method. You need to override it');
}
}
function CSSTransform(props){
var cssText = '', value;
for (var key in props) {
if (!props.hasOwnProperty(key)) continue;
value = props[key];
if (value == null) continue;
cssText += key + '(' + value + ') ';
}
return cssText;
}
function px(value){
if (typeof value == 'number') return value + 'px';
if (Array.isArray(value)) return value.map(px);
return value;
}
function valueFromRangeToRange(value, fromMin, fromMax, toMin, toMax){
if (typeof value != number) return value;
var fromRange = fromMax - fromMin;
var toRange = toMax - toMin;
return (((fromValue - fromMin) * toRange) / fromRange) + toMin;
}
function Layer(props, firstChild){
var children = firstChild && Array.prototype.slice.call(arguments, 1);
var origin = props.origin;
var transform = CSSTransform({
matrix: props.matrix || props.m,
matrix3d: props.matrix3d || props.m3d,
translate: px(props.translate || props.t),
translate3d: px(props.translate3d || props.t3d),
translateX: px(props.translateX || props.tX),
translateY: px(props.translateY || props.tY),
translateZ: px(props.translateZ || props.tZ),
scale: props.scale || props.s,
scale3d: props.scale3d || props.s3d,
scaleX: props.scaleX || props.sX,
scaleY: props.scaleY || props.sY,
scaleZ: props.scaleZ || props.sZ,
skewX: props.skewX || props.kX,
skewY: props.skewY || props.sY,
rotate: props.rotate || props.r,
rotate3d: props.rotate3d || props.r3d,
rotateX: props.rotateX || props.rX,
rotateY: props.rotateY || props.rY,
rotateZ: props.rotateZ || props.rZ,
perspective: props.perspective || props.p,
});
var filter = CSSTransform({
"grayscale": props.grayscale,
"sepia": props.sepia,
"saturate": props.saturate,
"hue-rotate": props.hue,
"invert": props.invert,
"opacity": props.opacity,
"brightness": props.brightness,
"contrast": props.contrast,
"blur": px(props.blur),
"drop-shadow": props.shadow,
});
var style = {
display: 'inline-block',
position: 'absolute',
backgroundColor: props.fill || '#f00',
opacity: props.alpha || props.opacity,
width: props.width || props.w,
height: props.height || props.h,
'pointer-events': props.touchable ? 'auto' : 'none',
'filter': filter,
'transform-origin': origin,
'transform': transform,
};
if (props.flow) style.top = style.left = 'auto';
else style.top = style.left = 0;
if ('top' in props) style.top = props.top;
if ('left' in props) style.left = props.left;
expandCSSCompat(style, 'filter');
expandCSSCompat(style, 'transform');
expandCSSCompat(style, 'transform-origin');
return React.DOM.div({style:style}, children);
}
function expandCSSCompat(style, key){
style['-webkit-' + key] = style[key];
style['-moz-' + key] = style[key];
style['-ms-' + key] = style[key];
style['-o-' + key] = style[key];
}
function Spring(config, state){
// config
var mass = state.mass = config.mass || state.mass || Spring.prototype.mass;
var friction = state.friction = config.friction || state.friction || Spring.prototype.friction;
var stiffness = state.stiffness = config.stiffness || state.stiffness || Spring.prototype.stiffness;
var precision = state.precision = config.precision || state.precision || Spring.prototype.precision || 1000;
var min = state.min = config.min || state.min || Spring.prototype.min;
var input = config.input || state.input || Spring.prototype.input;
input = Math.round((input * precision)) / precision;
var value = config.value || state.value || 0;
// state.targetValue = value;
var speed = config.speed || state.speed || 0;
// if (typeof value != 'number') throw TypeError('expected "value" to be a number');
var scale = config.scale || state.scale || 0;
if (!scale){
var ts = state.ts || Date.now();
state.ts = Date.now();
scale = (state.ts - ts) / 30 || 1;
}
// calculate
var distance = value - input;
if (!(distance > min && distance > -min
|| distance < min && distance < -min)){
if (!(speed > min && speed > -min
|| speed < min && speed < -min)){
distance = 0;
value = input;
speed = 0;
}
}
state.isDirty = distance + speed;
var dampingForce = -friction * speed;
var springForce = -stiffness * distance;
var totalForce = springForce + dampingForce;
var acceleration = totalForce / mass;
// output
state.speed = speed + acceleration * scale;
state.value = value + state.speed;
// state.value = Math.round((state.value * precision)) / precision;
return state.value
}
Spring.prototype.enabled = true;
Spring.prototype.mass = 10;
Spring.prototype.friction = 3;
Spring.prototype.stiffness = 120 / 100;
Spring.prototype.precision = 100;
Spring.prototype.min = 0.01;
Spring.prototype.input = 0;