Skip to content

Instantly share code, notes, and snippets.

@kaosat-dev
Created February 19, 2017 00:11
Show Gist options
  • Save kaosat-dev/7af575680e7a96f2044ae8eebbd37a84 to your computer and use it in GitHub Desktop.
Save kaosat-dev/7af575680e7a96f2044ae8eebbd37a84 to your computer and use it in GitHub Desktop.
regl + cyclejs (onionify) test
import { run } from '@cycle/xstream-run';
import { button, div, input,
makeDOMDriver, h } from '@cycle/dom';
import xs from 'xstream';
import isolate from '@cycle/isolate';
import onionify from 'cycle-onionify';
import R from 'ramda';
import {pick, mix} from 'cycle-onionify'
import regl from 'regl'
function domEvent(sources, selector, event){
return sources.DOM.select(selector).events(event)
}
function makeStateAndReducers$ (actions$, actionFns, sources) {
const {init} = actionFns
const init$ = xs.of(init)
const elements = Object.keys(actions$).map(function (actionName$) {
const name = actionName$.replace('Action$', '')
const actFn = actionFns[name]
const act$ = actions$[actionName$]
.map(action => state => actFn(state, action))
return act$
})
const reducer$ = xs.merge(init$, ...elements)
const state$ = sources.onion.state$
return {state$, reducer$}
}
function mergeReducers(init, components){
return xs.merge(
xs.of(init), ...components.map(x=>x.onion))
}
function makeDefaultReducer(defaultstate={}){
return function defaultReducer(prevState) {
if (typeof prevState === 'undefined') {
return defaultstate // Parent didn't provide state for the child, so initialize it.
} else {
return prevState // Let's just use the state given from the parent.
}
}
}
function Counter(sources) {
const init = () => ({ count: 0, toggled: true, text:'yup, some text' })
const inc = (state, input) => ({...state, count: state.count + 1 })
const dec = (state, input) => ({...state, count: state.count - 1 })
const setText = (state, input) => ({...state, text: input })
const toggle = (state, input) => ({...state, toggled: !state.toggled })
const view = state => div([
div(state.count.toString()),
div('Toggled: '+state.toggled),
button('.toggle','toggle'),
state.toggled ? div('.toggleable', [
button('.inc', '+'),
button('.dec', '-'),
input('.input',{attrs: {type: 'text', value:state.text}})
]) : null
])
const _domEvent = domEvent.bind(null, sources)
const incAction$ = _domEvent('.inc', 'click')
const decAction$ = _domEvent('.dec', 'click')
const setTextAction$ = xs.merge(
_domEvent('.input', 'input'),
_domEvent('.input', 'change')
)
.map(e=>e.target.value)
const toggleAction$ = _domEvent('.toggle', 'click')
const {state$, reducer$} = makeStateAndReducers$({incAction$, decAction$, setTextAction$, toggleAction$}, {init, inc, dec, setText, toggle}, sources)
return {
DOM: state$.map(view),
onion: reducer$
}
}
function GLComponent(sources){
const init = makeDefaultReducer({v1:0})
let _regl
let render
let canvas
let initDone
function prepareRender(regl){
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
const draw = regl({
frag: `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}`,
vert: `
precision mediump float;
attribute vec2 position;
uniform float angle;
uniform vec2 offset;
void main() {
gl_Position = vec4(
cos(angle) * position.x + sin(angle) * position.y + offset.x,
-sin(angle) * position.x + cos(angle) * position.y + offset.y, 0, 1);
}`,
attributes: {
position: [
0.5, 0,
0, 0.5,
1, 1]
},
uniforms: {
// the batchId parameter gives the index of the command
color: ({tick}, props, batchId) => [
Math.sin(0.02 * ((0.1 + Math.sin(batchId)) * tick + 3.0 * batchId)),
Math.cos(0.02 * (0.02 * tick + 0.1 * batchId)),
Math.sin(0.02 * ((0.3 + Math.cos(2.0 * batchId)) * tick + 0.8 * batchId)),
1
],
angle: ({tick}) => 0.01 * tick,
offset: regl.prop('offset')
},
depth: {
enable: false
},
count: 3
})
return draw
}
const initialize = function initialize (vnode) {
console.log('initialize')
canvas = vnode.elm
_regl = regl({
canvas,
})
render = prepareRender(_regl)
initDone = true
}
const view = state => h('div.gl', [
div('stuff'),
h('canvas#drawArea', {
props: {width: 100, height: 100},
style:{left:`${state.left}px`, position:'absolute'},
hook: {
insert: initialize
}
})
])
const {state$, reducer$} = makeStateAndReducers$({}, {init}, sources)
state$.addListener({next:
function(state){
if(initDone){
//console.log('state',state)
_regl.clear({
color: [0, 0, 0, 1]
})
render({ offset: [-1, -state.v1] })
}
}
})
return {
DOM: state$.map(view),
onion: reducer$
}
}
function main(sources){
const init = () => ({1: {}, 2:{},gl2:{left:20}, gl:{left:100, v1:0} })
const counter1 = isolate(Counter, 1)(sources)
const counter2 = isolate(Counter, 2)(sources)
const counter3 = isolate(Counter, 'gna')(sources)
const gl = isolate(GLComponent,'gl')(sources)
const gl2 = isolate(GLComponent,'gl2')(sources)
const state$ = sources.onion.state$
const reducer$ = xs.merge(
mergeReducers(init, [counter1, counter2, counter3, gl, gl2]),
xs.periodic(3000).map(i => function reducer(state) {
return {...state, gl2:{v1:i/100.0},gl: {v1:i/200.0}}
})
)
const vdom$ = xs.combine(state$, counter1.DOM, counter2.DOM, counter3.DOM, gl.DOM, gl2.DOM)
.map(([ state, counter1, counter2, counter3, gl, gl2 ]) => div([
div(JSON.stringify(state, null, 4)),
counter1,
counter2,
counter3,
gl,
gl2
]))
return {
DOM: vdom$,
onion: reducer$
}
}
const wrappedMain = onionify(main)
run(wrappedMain, {
DOM: makeDOMDriver('#app')
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment