fune/devtools/client/inspector/animation-old/components/keyframes.js
Julian Descottes 640fe52298 Bug 1454696 - Run eslint --fix for prefer-const;r=yulia
MozReview-Commit-ID: F6xUXCgdRE4

--HG--
extra : rebase_source : 65de1b0aba412d9044b5196115f74276caa058f2
2018-06-01 12:36:09 +02:00

236 lines
7.4 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {createNode, createSVGNode} =
require("devtools/client/inspector/animation-old/utils");
const { ProgressGraphHelper, } =
require("devtools/client/inspector/animation-old/graph-helper.js");
// Counter for linearGradient ID.
let LINEAR_GRADIENT_ID_COUNTER = 0;
/**
* UI component responsible for displaying a list of keyframes.
* Also, shows a graphical graph for the animation progress of one iteration.
*/
function Keyframes() {}
exports.Keyframes = Keyframes;
Keyframes.prototype = {
init: function(containerEl) {
this.containerEl = containerEl;
this.keyframesEl = createNode({
parent: this.containerEl,
attributes: {"class": "keyframes"}
});
},
destroy: function() {
this.keyframesEl.remove();
this.containerEl = this.keyframesEl = this.animation = null;
},
render: function({keyframes, propertyName, animation, animationType}) {
this.keyframes = keyframes;
this.propertyName = propertyName;
this.animation = animation;
// Create graph element.
const graphEl = createSVGNode({
parent: this.keyframesEl,
nodeType: "svg",
attributes: {
"preserveAspectRatio": "none"
}
});
// This visual is only one iteration,
// so we use animation.state.duration as total duration.
const totalDuration = animation.state.duration;
// Minimum segment duration is the duration of one pixel.
const minSegmentDuration =
totalDuration / this.containerEl.clientWidth;
// Create graph helper to render the animation property graph.
const win = this.containerEl.ownerGlobal;
const graphHelper =
new ProgressGraphHelper(win, propertyName, animationType, keyframes, totalDuration);
renderPropertyGraph(graphEl, totalDuration, minSegmentDuration, graphHelper);
// Destroy ProgressGraphHelper resources.
graphHelper.destroy();
// Set viewBox which includes invisible stroke width.
// At first, calculate invisible stroke width from maximum width.
// The reason why divide by 2 is that half of stroke width will be invisible
// if we use 0 or 1 for y axis.
const maxStrokeWidth =
win.getComputedStyle(graphEl.querySelector(".keyframes svg .hint")).strokeWidth;
const invisibleStrokeWidthInViewBox =
maxStrokeWidth / 2 / this.containerEl.clientHeight;
graphEl.setAttribute("viewBox",
`0 -${ 1 + invisibleStrokeWidthInViewBox }
${ totalDuration }
${ 1 + invisibleStrokeWidthInViewBox * 2 }`);
// Append elements to display keyframe values.
this.keyframesEl.classList.add(animation.state.type);
for (const frame of this.keyframes) {
createNode({
parent: this.keyframesEl,
attributes: {
"class": "frame",
"style": `left:${frame.offset * 100}%;`,
"data-offset": frame.offset,
"data-property": propertyName,
"title": frame.value
}
});
}
}
};
/**
* Render a graph representing the progress of the animation over one iteration.
* @param {Element} parentEl - Parent element of this appended path element.
* @param {Number} duration - Duration of one iteration.
* @param {Number} minSegmentDuration - Minimum segment duration.
* @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHalper.
*/
function renderPropertyGraph(parentEl, duration, minSegmentDuration, graphHelper) {
const segments = graphHelper.createPathSegments(duration, minSegmentDuration);
const graphType = graphHelper.getGraphType();
if (graphType !== "color") {
graphHelper.appendShapePath(parentEl, segments, graphType);
renderEasingHint(parentEl, segments, graphHelper);
return;
}
// Append the color to the path.
segments.forEach(segment => {
segment.y = 1;
});
const path = graphHelper.appendShapePath(parentEl, segments, graphType);
const defEl = createSVGNode({
parent: parentEl,
nodeType: "def"
});
const id = `color-property-${ LINEAR_GRADIENT_ID_COUNTER++ }`;
const linearGradientEl = createSVGNode({
parent: defEl,
nodeType: "linearGradient",
attributes: {
"id": id
}
});
segments.forEach(segment => {
createSVGNode({
parent: linearGradientEl,
nodeType: "stop",
attributes: {
"stop-color": segment.style,
"offset": segment.x / duration
}
});
});
path.style.fill = `url(#${ id })`;
renderEasingHintForColor(parentEl, graphHelper);
}
/**
* Renders the easing hint.
* This method renders an emphasized path over the easing path for a keyframe.
* It appears when hovering over the easing.
* It also renders a tooltip that appears when hovering.
* @param {Element} parentEl - Parent element of this appended path element.
* @param {Array} path segments - [{x: {Number} time, y: {Number} progress}, ...]
* @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHelper.
*/
function renderEasingHint(parentEl, segments, helper) {
const keyframes = helper.getKeyframes();
const duration = helper.getDuration();
// Split segments for each keyframe.
for (let i = 0, indexOfSegments = 0; i < keyframes.length - 1; i++) {
const startKeyframe = keyframes[i];
const endKeyframe = keyframes[i + 1];
const endTime = endKeyframe.offset * duration;
const keyframeSegments = [];
for (; indexOfSegments < segments.length; indexOfSegments++) {
const segment = segments[indexOfSegments];
keyframeSegments.push(segment);
if (startKeyframe.offset === endKeyframe.offset) {
keyframeSegments.push(segments[++indexOfSegments]);
break;
} else if (segment.x === endTime) {
break;
}
}
// Append easing hint as text and emphasis path.
const gEl = createSVGNode({
parent: parentEl,
nodeType: "g"
});
createSVGNode({
parent: gEl,
nodeType: "title",
textContent: startKeyframe.easing
});
helper.appendLinePath(gEl, keyframeSegments, `${helper.getGraphType()} hint`);
}
}
/**
* Render easing hint for properties that are represented by color.
* This method render as text only.
* @param {Element} parentEl - Parent element of this appended path element.
* @param {ProgressGraphHelper} graphHelper - The object of ProgressGraphHalper.
*/
function renderEasingHintForColor(parentEl, helper) {
const keyframes = helper.getKeyframes();
const duration = helper.getDuration();
// Split segments for each keyframe.
for (let i = 0; i < keyframes.length - 1; i++) {
const startKeyframe = keyframes[i];
const startTime = startKeyframe.offset * duration;
const endKeyframe = keyframes[i + 1];
const endTime = endKeyframe.offset * duration;
// Append easing hint.
const gEl = createSVGNode({
parent: parentEl,
nodeType: "g"
});
createSVGNode({
parent: gEl,
nodeType: "title",
textContent: startKeyframe.easing
});
createSVGNode({
parent: gEl,
nodeType: "rect",
attributes: {
x: startTime,
y: -1,
width: endTime - startTime,
height: 1,
class: "hint",
}
});
}
}