Created
November 24, 2016 06:50
-
-
Save appsforartists/39b83156dee562636d5ddcec200569eb to your computer and use it in GitHub Desktop.
Attempting drag in Cycle.js
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
/** @license | |
* Copyright 2016 - present The Material Motion Authors. All Rights Reserved. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
* use this file except in compliance with the License. You may obtain a copy | |
* of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
* License for the specific language governing permissions and limitations | |
* under the License. | |
*/ | |
import pairwise from 'xstream/extra/pairwise' | |
import sampleCombine from 'xstream/extra/sampleCombine' | |
import { DOMSource } from '@cycle/dom/rxjs-typings'; | |
import xs, { Stream } from 'xstream' | |
import { VNode } from '@cycle/dom'; | |
import { html } from 'snabbdom-jsx'; | |
export type Sources = { | |
DOM: DOMSource | |
} | |
export type Sinks = { | |
DOM: Stream<VNode> | |
} | |
// helpers moved to bottom of file for brevity | |
export function App(sources: Sources): Sinks { | |
const drag$ = getDrag$FromDOMSource( | |
sources.DOM.select('.draggable') | |
); | |
// This looks like the right way to implement mutual observation according to | |
// http://staltz.com/xstream/#imitate but it isn't working. Moreover, if I | |
// even subscribe to location$Proxy, vtree$ stops dispatching. | |
const location$Proxy = xs.create(); | |
const location$ = xs.merge( | |
xs.of( | |
{ | |
x: 0, | |
y: 0, | |
} | |
), | |
drag$.compose( | |
sampleCombine(location$Proxy) | |
).map( | |
addPoints | |
) | |
); | |
location$Proxy.imitate(location$); | |
const vtree$ = location$.map( | |
location => ( | |
<div | |
className = 'draggable' | |
style = { | |
{ | |
backgroundColor: '#8BC34A', | |
width: '56px', | |
height: '56px', | |
borderRadius: '28px', | |
boxShadow: ` | |
0 3px 1px -2px rgba(0, 0, 0, 0.2), | |
0 2px 2px 0 rgba(0, 0, 0, 0.14), | |
0 1px 5px 0 rgba(0, 0, 0, 0.12) | |
`, | |
willChange: 'transform', | |
transform: `translate(${ location.x }px, ${ location.y }px)`, | |
} | |
} | |
/> | |
) | |
); | |
const sinks = { | |
DOM: vtree$ | |
}; | |
return sinks; | |
} | |
function getPointerLocationFromEvent(event: PointerEvent) { | |
return { | |
x: event.pageX, | |
y: event.pageY, | |
}; | |
} | |
function getDistanceBetweenPoints([prev, next]) { | |
if (!prev || !next) { | |
return { | |
x: 0, | |
y: 0, | |
}; | |
} | |
return { | |
x: next.x - prev.x, | |
y: next.y - prev.y, | |
}; | |
} | |
function addPoints([prev, next]) { | |
if (!prev || !next) { | |
return { | |
x: 0, | |
y: 0, | |
}; | |
} | |
return { | |
x: next.x + prev.x, | |
y: next.y + prev.y, | |
}; | |
} | |
function getDrag$FromDOMSource(domSource) { | |
return domSource.events('pointerdown').map( | |
downEvent => { | |
downEvent.target.setPointerCapture(downEvent.pointerId); | |
return domSource.events('pointermove').endWhen( | |
domSource.events('pointerup').take() | |
).startWith( | |
downEvent | |
).map( | |
getPointerLocationFromEvent | |
).compose(pairwise).map( | |
getDistanceBetweenPoints | |
) | |
} | |
).flatten(); | |
} |
And the cleaner solution:
export function App(sources: Sources): Sinks {
const drag$ = getDrag$FromDOMSource(
sources.DOM.select('.draggable')
);
// drags are relative to the last known location, but location$ doesn't exist
// yet, so we define it in terms of a proxy that will be filled later with a
// stream of (drag + last location).
//
// Proxying drag instead of location, because proxy.imitate doesn't work with
// memory streams like startWith
const combinedDrag$Proxy = xs.create();
const location$ = xs.merge(
combinedDrag$Proxy
).startWith(
{
x: 0,
y: 0,
}
);
combinedDrag$Proxy.imitate(
drag$.compose(
sampleCombine(location$)
).map(
addPoints
)
);
const vtree$ = location$.map(
location => (
<div
className = 'draggable'
style = {
{
backgroundColor: '#8BC34A',
width: '56px',
height: '56px',
borderRadius: '28px',
boxShadow: `
0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12)
`,
willChange: 'transform',
transform: `translate(${ location.x }px, ${ location.y }px)`,
}
}
/>
)
);
const sinks = {
DOM: vtree$
};
return sinks;
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's because merge isn't a memorystream, so sampleCombine never gets the first point.
This is ugly, but it works: