Skip to content

Commit

Permalink
[added] MapScrollPositionToCSSValue plan + performer
Browse files Browse the repository at this point in the history
Summary: Closes #90

Reviewers: O2 Material Motion, O3 Material Motion JavaScript platform reviewers, shyndman

Reviewed By: O3 Material Motion JavaScript platform reviewers, shyndman

Subscribers: featherless, shyndman

Tags: #material_motion

Differential Revision: http://codereview.cc/D1817
  • Loading branch information
appsforartists committed Nov 2, 2016
1 parent b54d8cc commit b422d4c
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/** @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 {
Plan,
Performing,
PerformingArgs,
PerformingAddPlanArgs,
} from 'material-motion-runtime';

import MapScrollPositionToCSSValuePlan from './MapScrollPositionToCSSValuePlan';

export default class MapScrollPositionToCSSValuePerformer implements Performing {
_awaitingRedraw: boolean = false;
_target: HTMLElement;
_plan: MapScrollPositionToCSSValuePlan;

constructor({ target }:PerformingArgs) {
this._target = target as HTMLElement;

document.addEventListener('scroll', this._queueRedraw);
document.addEventListener('resize', this._queueRedraw);
}

addPlan({ plan }:{ plan: MapScrollPositionToCSSValuePlan }) {
this._plan = plan;
this._redraw();
}

_queueRedraw = () => {
if (!this._awaitingRedraw) {
requestAnimationFrame(this._redraw);
}

this._awaitingRedraw = true;
}

_redraw = () => {
const {
propertyName,
valueTemplate,
fromValue,
toValue,
scrollOffset = 0,
// We need to check scrollTop anyway to do the interpolating, so
// scrollHeight shouldn't cost anything extra.
scrollRange = document.body.scrollHeight - window.innerHeight,
} = this._plan;

const valueRange = toValue - fromValue;

if (!scrollRange) {
console.warn(
'MapScrollPositionToCSSValuePerformer cannot map from a scroll range of 0; aborting'
);
return;
}

// If multiple performer instances are listening to scroll, you'll end up
// reading and writing to the DOM in each one's _redraw. When this gets
// split into its own stepper, batch/cache scroll position measuring
// (perhaps using the event's timestamp) to avoid thrashing the DOM.
const scrollPosition = window.scrollY;

let result: number;

if (scrollPosition <= scrollOffset) {
result = fromValue;

} else if (scrollPosition >= scrollOffset + scrollRange) {
result = toValue;

} else {
result = fromValue + valueRange * (scrollPosition - scrollOffset) / scrollRange;
}

// element.style[key] = value doesn't work in TypeScript
// https://github.com/Microsoft/TypeScript/issues/11914
this._target.style.setProperty(propertyName, valueTemplate.replace(/__/g, result.toFixed(2)));

this._awaitingRedraw = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** @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 { Plan } from 'material-motion-runtime';

import MapScrollPositionToCSSValuePerformer from './MapScrollPositionToCSSValuePerformer';

interface MapScrollPositionToCSSValuePlanArgs {
propertyName: string;
valueTemplate: string;
fromValue: number;
toValue: number;
scrollOffset?: number;
scrollRange?: number;
}

class MapScrollPositionToCSSValuePlan {
_PerformerType = MapScrollPositionToCSSValuePerformer;

constructor(kwargs:MapScrollPositionToCSSValuePlanArgs) {
return Object.assign(this, kwargs);
}
}

// Until TypeScript has better support for destructuring, this is the DRY way
// to type named args.
//
// https://github.com/Microsoft/TypeScript/issues/5326#issuecomment-256787939
interface MapScrollPositionToCSSValuePlan extends MapScrollPositionToCSSValuePlanArgs, Plan {}
export default MapScrollPositionToCSSValuePlan;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
width: 100vw;
height: 300vh;
background: linear-gradient(#3367D6, #4285F4);
}

.layer {
will-change: transform;
position: fixed;
top: 0px;
left: 0px;
color: #E9E9E9;
}

.material-icons {
position: absolute;
}
</style>
<link href = "https://fonts.googleapis.com/icon?family=Material+Icons" rel = "stylesheet">
<script defer
src = "/parallax.js"
></script>
</head>
<body>
</body>
</html>
56 changes: 56 additions & 0 deletions packages/experiments/src/domain-mapping/examples/parallax/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/** @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 { Runtime } from 'material-motion-runtime';

import MapScrollPositionToCSSValuePlan from '../../MapScrollPositionToCSSValuePlan';

const runtime = new Runtime();
const layerCount = 20;

for (let i = 1; i <= layerCount; i++) {
const layer = document.createElement('div');
layer.className = 'layer';
layer.style.opacity = ((2 + i) / (3 + layerCount)).toFixed(2);

runtime.addPlan({
plan: new MapScrollPositionToCSSValuePlan({
propertyName: 'transform',
valueTemplate: 'translate(__vw, 0px)',
fromValue: 0,
toValue: 50 * i / layerCount,
}),
target: layer,
});

for (let n = 0; n < 150 / layerCount; n++) {
const icon = document.createElement('div');
icon.className = 'material-icons';
icon.innerText = 'cloud';
icon.style.transform = `
translate(
${ Math.round(Math.random() * 400 - 200) / 2 }vw,
${ Math.round(Math.random() * 200) / 2 }vh
)
scale(${ 3 + 4 * (i / layerCount) })
`;

layer.appendChild(icon);
}

document.body.appendChild(layer);
}
8 changes: 5 additions & 3 deletions packages/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ export interface PlanAndTarget {
target: Target;
}

export interface PerformingAddPlanArgs {
plan: Plan;
}

export interface Performing {
addPlan(kwargs: {
plan: Plan
}): void;
addPlan(kwargs: PerformingAddPlanArgs): void;
}

export interface PerformingArgs {
Expand Down

0 comments on commit b422d4c

Please sign in to comment.