@mvarble/viewport-utilities
v1.0.12
Published
A bunch of utilities for interfacing with the viewport.js API
Downloads
11
Readme
viewport-utilities
A bunch of utilities for interfacing with the viewport.js API.
API
relativeMousePosition
[x, y] = relativeMousePosition(event)
This function calculates the bounding rectangle of the event
target and calculates the mouse position relative to this.
If the isDrag
attribute from createDrag is present in the event, event.isDrag.target
is used instead of event.target
.
singleClick
singleclick$ = mousedown$.compose(singleClick)
This is a xstream operator that takes a stream of mousedown
events and returns a stream of click events that happened within 50ms and 3 pixels of each mousedown
.
The intention of this is to separate usual click
events from those returned by createDrag.
createDrag
drag$ = mousedown$.compose(createDrag)
This is a xstream operator that takes a stream of mousedown
events and returns a stream of streams that match the following diagram.
mousedown: |-----x-------------------x------------->
(createDrag)
mousemove: |-x-------x--x-x----x------------------->
mouseup: |----------------o-------------o-------->
output: |-----x-------------------x------------->
\ \
--x--x-x-o-| -----|
Note that every stream starts with a mousedown
event, and ends with the mouseup
event.
The streams will always output mousemove
and mouseup
events from the document, not the DOM element that the mousedown
events corresponded to.
However, each of these events will have an isDrag
attribute which will point to the reference of the original mousedown
event.
Also, these streams are not provided as arguments of the operator; they are just in the diagram for explanation.
Note in the example above, we have that the output streams will be empty if no mousemove
occurs between 'mousedown' and 'mouseup' events.
The rationale behind this is that:
- Clicks are not drags
- Every nonempty drag starts with a 'mousemove'.
- Every nonempty drag ends with a 'mouseup'.
renderBox
renderBox(context, frame, options)
This function will use the provided context
to perform an imperative render to the canvas with said context.
The render will display a rectangle with vertices having coordinates [-1, -1] and [1, 1] with respect to frame
.
By providing an object options
with options.fill
and options.stroke
, this will decide whether to do the respective operations in the render.
The options
object is optional, and it is assumed of its attributes both are true.
withWindow
NewComponent = withWindow(FrameComponent, options)
By providing a component FrameComponent
that is managing a frame using the withState API, this will return a new component which wraps the oldState
into an object of the form:
{
type: 'root',
width: w,
height: h,
children: [
{ type: 'window', worldMatrix: [...] },
oldState
],
}
This object will be such that its first child frame has a world matrix such that [-1, -1] and [1, 1] are the bottom-left and top-right coordinates of the canvas, respectively.
Moreover, this component will merge reducers into the state stream of FrameComponent
that trigger resize calculations and allow for navigating its frame within the window.
The returned NewComponent
will be such that there is a new source, a stream of desired dimensions [w, h]
of the component.
The resize will be calculated such that the child frame will be scaled uniformly in both dimensions with a scale that requires the least amount of distortion in accomodating the new dimension ratio w/h
.
The options
object sets the keys of the sources/sinks NewComponent
necessarily uses.
Its keys are the following:
state
: This is the desired key of thewithState
source/sinks. By default, it is'state'
.dimensions
: This is the desired source key of the stream of desired dimensions. By default, it is'dimensions'
.frameSource
: This is the desired source key of the mounted FrameSource instance is required for delivering mouse intent for panning/resizing the child frame. By default, it is'frameSource'
.
resizeFrame
newFrame = resizeFrame(frame, width, height)
This will take a state frame
of the form of withWindow and return newFrame
, accomodated to the new dimensions width
and height
.
This is used in the reducer of the component returned by said wrapper.
changeZoom
newFrame = changeZoom(event, frame)
This will take a wheel
event event
and a state frame
of the form of withWindow and return newFrame
, in which frame.children[1]
has been scaled according to the direction of the wheel move.
This is used in the reducer of the component returned by said wrapper.
parentDims
dimensions$ = element$.compose(parentDims)
This is a xstream operator that takes a stream of DOM elements and returns a stream of [offsetWidth, offsetHeight]
dimensions corresponding to the parent of the element in the stream.
The initial dimensions are returned with the delivery of the element of the stream element$
; thereafter, window resize
events will pipe to this stream only when the dimensions have changed.
To this end, you may think of this as a flattened stream of streams of the form
|---[initialWidth, initialHeight]----[newWidth, newHeight]--[newWidth, newHeight]--->
Warning. If you declare the canvas size dependent on this stream (and make your imperative render function resize accordingly) and the parent does not have fixed sizing, you may cause an indefinite loop of
parent dimensions change → parentDims update → canvas dimensions change → parent dimensions change → ...
so tread lightly with setting the parent's dimensions in some fixed way (say, with CSS
vw
andvh
).