Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(react) deck renderer components #275

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions examples/website/quick-start/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {anticipate, easeIn, reverseEasing} from 'popmotion';
const INITIAL_VIEW_STATE = {
longitude: -122.402,
latitude: 37.79,
zoom: 11
zoom: 14
};

const TIMECODE = {
Expand All @@ -17,10 +17,23 @@ const TIMECODE = {
};

const RESOLUTION = {
width: 640,
height: 480
width: 1920,
height: 1080
};

const FORMAT_CONFIGS = {
webm: {
quality: 0.99
},
gif: {
width: 480,
height: 270
}
};

const BACKGROUND = [30 / 255, 30 / 255, 30 / 255, 1];
const BLUE = [37, 80, 129];

const Container = ({children}) => (
<div
style={{
Expand All @@ -44,17 +57,18 @@ export default function App() {
a.applyLayerKeyframes([
new ScatterplotLayer({
id: 'circle',
data: [{position: [-122.402, 37.79], color: [255, 0, 0], radius: 1000}],
data: [{position: [-122.402, 37.79], color: BLUE, radius: 1000}],
getFillColor: d => d.color,
getRadius: d => d.radius,
opacity: 0.1,
radiusScale: 0.01
opacity: 1,
radiusScale: 1
}),
new TextLayer({
id: 'text',
data: [{position: [-122.402, 37.79], text: 'Hello World'}],
opacity: 0.1,
getAngle: -90
opacity: 1,
getPixelOffset: [0, -64],
getSize: 64
})
]),
layerKeyframes: [
Expand Down Expand Up @@ -89,10 +103,14 @@ export default function App() {
<QuickAnimation
initialViewState={INITIAL_VIEW_STATE}
resolution={RESOLUTION}
formatConfigs={FORMAT_CONFIGS}
animation={animation}
deckProps={{
useDevicePixels: 1, // Otherwise retina displays will double resolution.
// Visualization Props
parameters: {
clearColor: [255, 255, 255, 1]
// Background color. Most video formats don't fully support transparency
clearColor: BACKGROUND
}
}}
timecode={TIMECODE}
Expand Down
1 change: 1 addition & 0 deletions modules/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"react-map-gl": "^5.2.10",
"react-modal": "^3.8.1",
"react-onclickoutside": "^6.9.0",
"react-virtualized-auto-sizer": "^1.0.7",
"reselect": "^3.0.0",
"styled-components": "^6.1.11"
},
Expand Down
31 changes: 18 additions & 13 deletions modules/react/src/components/quick-animation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {useState, useRef, useMemo} from 'react';
import DeckGL, {DeckGLRef} from '@deck.gl/react/typed';
import BasicControls from './basic-controls';
import {useDeckAdapter, useNextFrame} from '../hooks';
import {DeckGLRender} from '../renderer/deckgl-renderer';
import type {MapViewState} from '@deck.gl/core/typed';
import {FormatConfigs} from '@hubble.gl/core';

Expand Down Expand Up @@ -53,19 +54,23 @@ export const QuickAnimation = ({
return (
<>
<div style={{position: 'relative'}}>
<DeckGL
ref={deckRef}
style={{position: 'unset'}}
viewState={cameraFrame}
onViewStateChange={({viewState: vs}) => {
setCameraFrame(vs as MapViewState);
}}
controller={true}
width={resolution.width}
height={resolution.height}
layers={layers}
{...adapter.getProps({deck, onNextFrame, extraProps: deckProps})}
/>
<DeckGLRender renderResolution={resolution}>
{deckglStyle => (
<DeckGL
ref={deckRef}
style={deckglStyle}
viewState={cameraFrame}
onViewStateChange={({viewState: vs}) => {
setCameraFrame(vs as MapViewState);
}}
controller={true}
width={resolution.width}
height={resolution.height}
layers={layers}
{...adapter.getProps({deck, onNextFrame, extraProps: deckProps})}
/>
)}
</DeckGLRender>
</div>
<BasicControls
adapter={adapter}
Expand Down
52 changes: 52 additions & 0 deletions modules/react/src/renderer/deckgl-renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import {Pillarbox} from './pillarbox';
import {scale} from './utils';

// Render similar viewport boundary regardless of internal canvas size.
// The viewport bounds change with canvas size, so constrain it around
// a square (default 1080px). It can be wide, tall, or square.
const getCanvasClientSize = (internalCanvasResolution, minAxis = 1080) => {
const aspect = internalCanvasResolution.width / internalCanvasResolution.height;
if (aspect > 1) {
// horizontal
return {width: Math.round(minAxis * aspect), height: minAxis};
} else if (aspect < 1) {
// vertical
return {width: minAxis, height: Math.round(minAxis / aspect)};
}
// square
return {width: minAxis, height: minAxis};
};

export const DeckGLRender = ({renderResolution, previewPadding = 0, overlay = null, children}) => {
const canvasClientSize = getCanvasClientSize(renderResolution);
return (
<Pillarbox
internalCanvasResolution={renderResolution}
previewPadding={previewPadding}
overlay={overlay}
>
{previewSize => {
const scalar = scale(previewSize, canvasClientSize);
const deckglStyle = {
width: `${canvasClientSize.width}px`,
height: `${canvasClientSize.height}px`,
transform: `scale(${scalar})`,
transformOrigin: 'top left'
};
return (
<div
id="map-render"
style={{
width: `${previewSize.width}px`,
height: `${previewSize.height}px`,
position: 'relative'
}}
>
{children(deckglStyle)}
</div>
);
}}
</Pillarbox>
);
};
75 changes: 75 additions & 0 deletions modules/react/src/renderer/pillarbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, {useCallback} from 'react';
import styled from 'styled-components';
import AutoSizer from 'react-virtualized-auto-sizer';
import {nearestEven, scale} from './utils';

const AutoSizePillarbox = ({children, internalCanvasResolution, previewPadding}) => {
const getPreviewSize = useCallback(
({width, height}) => {
// padding
if (width > height) {
width = width - previewPadding;
} else {
height = height - previewPadding;
}
const scalar = scale({width, height}, internalCanvasResolution);
return {
width: nearestEven(internalCanvasResolution.width * scalar, 0),
height: nearestEven(internalCanvasResolution.height * scalar, 0)
};
},
[internalCanvasResolution, previewPadding]
);

return (
<AutoSizer>
{({width, height}) => {
const previewSize = getPreviewSize({width, height});
return children({
previewSize,
pillarboxSize: {width: nearestEven(width, 0), height: nearestEven(height, 0)}
});
}}
</AutoSizer>
);
};

const PillarboxBackground = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: ${props => props.size.width}px;
height: ${props => props.size.height}px;
`;

const PillarboxOverlay = styled.div`
position: absolute;
width: ${props => props.size.width}px;
height: ${props => props.size.height}px;
`;

export const Pillarbox = ({children, overlay, internalCanvasResolution, previewPadding}) => {
return (
<AutoSizePillarbox
internalCanvasResolution={internalCanvasResolution}
previewPadding={previewPadding}
>
{({previewSize, pillarboxSize}) => (
<PillarboxBackground size={pillarboxSize}>
{children ? (
children(previewSize)
) : (
<div
style={{
width: previewSize.width,
height: previewSize.height,
backgroundColor: 'green'
}}
/>
)}
{overlay && <PillarboxOverlay size={pillarboxSize}>{overlay}</PillarboxOverlay>}
</PillarboxBackground>
)}
</AutoSizePillarbox>
);
};
10 changes: 10 additions & 0 deletions modules/react/src/renderer/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const nearestEven = (num, decimalPlaces = 3) => {
const factorTen = Math.pow(10, decimalPlaces);
num = num * factorTen;
num = 2 * Math.round(num / 2);
return num / factorTen;
};

export function scale(startingSize, targetSize) {
return Math.min(startingSize.width / targetSize.width, startingSize.height / targetSize.height);
}
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3317,9 +3317,9 @@ camelize@^1.0.0:
integrity sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==

caniuse-lite@^1.0.30001370:
version "1.0.30001374"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz"
integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==
version "1.0.30001414"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz"
integrity sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg==

caseless@~0.12.0:
version "0.12.0"
Expand Down
Loading