Created
September 11, 2018 19:12
-
-
Save zaceno/5882691f810be89d0a0df0d58bec1d0e to your computer and use it in GitHub Desktop.
HAV2 Intro - example 11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<style> | |
.gauge { | |
position: relative; | |
padding: 0; | |
border: 1px #555 solid; | |
background-color: #aaa; | |
width: 200px; | |
height: 40px; | |
border-radius: 4px; | |
overflow: hidden; | |
} | |
.gauge-meter { | |
position: absolute; | |
padding: 0; | |
margin: 0; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
width: 62%; | |
background-color: hotpink; | |
} | |
.gauge-text { | |
position: absolute; | |
margin: 0; | |
padding: 0; | |
line-height: 40px; | |
font-size: 25px; | |
text-align: center; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
width: 100%; | |
color: #fff; | |
text-shadow: #000 0px -1px 1px; | |
z-index: 1000; | |
} | |
</style> | |
<script defer type="module"> | |
import {h, app} from "https://rawgit.com/hyperapp/hyperapp/V2/src/index.js" | |
const DURATION = 15000 //ms = 15s | |
const AnimationFrame = (() => { | |
const effect = (props, dispatch) => { | |
var frameId | |
const onFrame = (timestamp) => { | |
dispatch(props.action, timestamp) | |
nextFrame() | |
} | |
const nextFrame = () => { | |
frameId = requestAnimationFrame(onFrame) | |
} | |
nextFrame() | |
return () => cancelAnimationFrame(frameId) | |
} | |
return props => ({effect, ...props}) | |
})() | |
const Alert = (() => { | |
const effect = (props, dispatch) => { | |
alert(props.message) | |
} | |
return props => ({effect, ...props}) | |
})() | |
const Start = (state, event) => { | |
if (state.mode !== 'stopped') return | |
return { | |
mode: 'running', | |
startedTime: event.timeStamp, | |
remainingTime: DURATION, | |
duration: DURATION, | |
} | |
} | |
const Pause = state => { | |
if (state.mode !== 'running') return | |
return { | |
...state, | |
mode: 'paused', | |
} | |
} | |
const Continue = (state, event) => { | |
if (state.mode !== 'paused') return | |
return { | |
...state, | |
mode: 'running', | |
startedTime: event.timeStamp, | |
duration: state.remainingTime, | |
} | |
} | |
const Cancel = state => { | |
return { | |
...state, | |
mode: 'stopped' | |
} | |
} | |
const UpdateTime = (state, timestamp) => { | |
if (state.mode !== 'running') return | |
if (state.remainingTime < 0) return Cancel(state) | |
return { | |
...state, | |
remainingTime: state.duration - (timestamp - state.startedTime) | |
} | |
} | |
const Gauge = (props, children) => h('div', { class: 'gauge'}, [ | |
h('div', { | |
class: 'gauge-meter', | |
style: { | |
width: ( | |
props.active | |
? (100 * props.fraction) + '%' | |
: '100%' | |
), | |
} | |
}), | |
props.active && h('p', { class: 'gauge-text'}, children) | |
]) | |
const TimerControls = ({mode, timer}) => h('p', {}, [ | |
mode === 'stopped' | |
? h('button', {onClick: [StartX, {timer}] }, ['START']) | |
: h('button', {onClick: [CancelX, {timer}] }, ['CANCEL']), | |
mode === 'paused' | |
? h('button', {onClick: [ContinueX, {timer}] }, ['CONT.']) | |
: h('button', { | |
disabled: mode === 'stopped', | |
onClick: [PauseX, {timer}] | |
}, ['PAUSE']), | |
]) | |
const timerAction = (action, state, param, data) => { | |
const returnValue = action(state[param.timer], data) | |
if (!returnValue) return | |
return { | |
...state, | |
[param.timer]: { ...returnValue }, | |
} | |
} | |
const StartX = (...arg) => timerAction(Start, ...arg) | |
const CancelX = (...arg) => timerAction(Cancel, ...arg) | |
const PauseX = (...arg) => timerAction(Pause, ...arg) | |
const ContinueX = (...arg) => timerAction(Continue, ...arg) | |
const UpdateTimeX = (state, param, timestamp) => { | |
const nextState = timerAction(UpdateTime, state, param, timestamp) | |
const prevMode = state[param.timer].mode | |
const nextMode = nextState[param.timer].mode | |
if (prevMode === 'running' && nextMode === 'stopped') { | |
return [ nextState, Alert({ message: `Timer ${param.timer === 'timer1' ? 'A' : 'B'} timed out!` }) ] | |
} else { | |
return nextState | |
} | |
} | |
app({ | |
init: state => ({ | |
timer1: { | |
mode: 'stopped' | |
}, | |
timer2: { | |
mode: 'stopped' | |
} | |
}), | |
view: state => h('table', {}, [ | |
h('tr', {}, [ | |
h('td', {}, ['A']), | |
h('td', {}, [ | |
h( | |
Gauge, | |
{ | |
active: state.timer1.mode !== 'stopped', | |
fraction: state.timer1.remainingTime / DURATION, | |
}, | |
[ | |
Math.ceil(state.timer1.remainingTime / 1000), | |
' s' | |
] | |
) | |
]), | |
h('td', {}, [ | |
h(TimerControls, {mode: state.timer1.mode, timer: 'timer1'}), | |
]) | |
]), | |
h('tr', {}, [ | |
h('td', {}, ['B']), | |
h('td', {}, [ | |
h( | |
Gauge, | |
{ | |
active: state.timer2.mode !== 'stopped', | |
fraction: state.timer2.remainingTime / DURATION, | |
}, | |
[ | |
Math.ceil(state.timer2.remainingTime / 1000), | |
' s' | |
] | |
) | |
]), | |
h('td', {}, [ | |
h(TimerControls, {mode: state.timer2.mode, timer: 'timer2'}), | |
]) | |
]), | |
]), | |
container: document.body, | |
subscriptions: state => [ | |
state.timer1.mode === 'running' && AnimationFrame({action: [UpdateTimeX, {timer: 'timer1'}] }), | |
state.timer2.mode === 'running' && AnimationFrame({action: [UpdateTimeX, {timer: 'timer2'}] }), | |
] | |
}) | |
</script> | |
</head> | |
<body></body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment