UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

101 lines (85 loc) 3.43 kB
/* @license * Copyright 2019 Google LLC. 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. */ // Adapted from https://gist.github.com/gre/1650294 export const easeInOutQuad: TimingFunction = (t: number) => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t; /** * A TimingFunction accepts a value from 0-1 and returns a corresponding * interpolated value */ export type TimingFunction = (time: number) => number; /** * Creates a TimingFunction that uses a given ease to interpolate between * two configured number values. */ export const interpolate = (start: number, end: number, ease: TimingFunction = easeInOutQuad): TimingFunction => (time: number) => start + (end - start) * ease(time); /** * Creates a TimingFunction that interpolates through a weighted list * of other TimingFunctions ("tracks"). Tracks are interpolated in order, and * allocated a percentage of the total time based on their relative weight. */ export const sequence = (tracks: Array<TimingFunction>, weights: Array<number>): TimingFunction => { const totalWeight = weights.reduce((total, weight) => total + weight, 0); const ratios: Array<number> = weights.map(weight => weight / totalWeight); return (time: number) => { let start: number = 0; let ratio: number = Infinity; let track: TimingFunction = () => 0; for (let i = 0; i < ratios.length; ++i) { ratio = ratios[i]; track = tracks[i]; if (time <= (start + ratio)) { break; } start += ratio; } return track!((time - start) / ratio!); } }; /** * A Keyframe groups a target value, the number of frames to interpolate towards * that value and an optional easing funnction to use for interpolation. */ export interface Keyframe { value: number; frames: number; ease?: TimingFunction; } /** * Creates a "timeline" TimingFunction out of an initial value and a series of * Keyframes. The timeline function accepts value from 0-1 and returns the * current value based on keyframe interpolation across the total number of * frames. Frames are only used to indicate the relative length of each keyframe * transition, so interpolated values will be computed for fractional frames. */ export const timeline = (initialValue: number, keyframes: Array<Keyframe>): TimingFunction => { const tracks: Array<TimingFunction> = []; const weights: Array<number> = []; let lastValue = initialValue; for (let i = 0; i < keyframes.length; ++i) { const keyframe = keyframes[i]; const {value, frames} = keyframe; const ease = keyframe.ease || easeInOutQuad; const track = interpolate(lastValue, value, ease); tracks.push(track); weights.push(frames); lastValue = value; } return sequence(tracks, weights); };