Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / elements / BezierEditor.js
blob2e2c197d715e8ca4b99986af32de33bcada8bbb8
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6 * @constructor
7 * @extends {WebInspector.VBox}
8 */
9 WebInspector.BezierEditor = function()
11 WebInspector.VBox.call(this, true);
12 this.registerRequiredCSS("elements/bezierEditor.css");
13 this.contentElement.tabIndex = 0;
15 // Preview UI
16 this._previewElement = this.contentElement.createChild("div", "bezier-preview-container");
17 this._previewElement.createChild("div", "bezier-preview-animation");
18 this._previewElement.addEventListener("click", this._startPreviewAnimation.bind(this));
19 this._previewOnion = this.contentElement.createChild("div", "bezier-preview-onion");
20 this._previewOnion.addEventListener("click", this._startPreviewAnimation.bind(this));
22 this._outerContainer = this.contentElement.createChild("div", "bezier-container");
24 // Presets UI
25 this._presetsContainer = this._outerContainer.createChild("div", "bezier-presets");
26 this._presetUI = new WebInspector.BezierUI(40, 40, 0, 2, false);
27 this._presetCategories = [];
28 for (var i = 0; i < WebInspector.BezierEditor.Presets.length; i++) {
29 this._presetCategories[i] = this._createCategory(WebInspector.BezierEditor.Presets[i]);
30 this._presetsContainer.appendChild(this._presetCategories[i].icon);
33 // Curve UI
34 this._curveUI = new WebInspector.BezierUI(150, 250, 50, 7, true);
35 this._curve = this._outerContainer.createSVGChild("svg", "bezier-curve");
36 WebInspector.installDragHandle(this._curve, this._dragStart.bind(this), this._dragMove.bind(this), this._dragEnd.bind(this), "default");
38 this._header = this.contentElement.createChild("div", "bezier-header");
39 var minus = this._createPresetModifyIcon(this._header, "bezier-preset-minus", "M 12 6 L 8 10 L 12 14");
40 var plus = this._createPresetModifyIcon(this._header, "bezier-preset-plus", "M 8 6 L 12 10 L 8 14");
41 minus.addEventListener("click", this._presetModifyClicked.bind(this, false));
42 plus.addEventListener("click", this._presetModifyClicked.bind(this, true));
43 this._label = this._header.createChild("span", "source-code bezier-display-value");
46 WebInspector.BezierEditor.Events = {
47 BezierChanged: "BezierChanged"
50 WebInspector.BezierEditor.Presets = [
52 { name: "ease-in-out", value: "ease-in-out" },
53 { name: "In Out · Sine", value: "cubic-bezier(0.45, 0.05, 0.55, 0.95)" },
54 { name: "In Out · Quadratic", value: "cubic-bezier(0.46, 0.03, 0.52, 0.96)" },
55 { name: "In Out · Cubic", value: "cubic-bezier(0.65, 0.05, 0.36, 1)" },
56 { name: "Fast Out, Slow In", value: "cubic-bezier(0.4, 0, 0.2, 1)" },
57 { name: "In Out · Back", value: "cubic-bezier(0.68, -0.55, 0.27, 1.55)" }
60 { name: "Fast Out, Linear In", value: "cubic-bezier(0.4, 0, 1, 1)" },
61 { name: "ease-in", value: "ease-in" },
62 { name: "In · Sine", value: "cubic-bezier(0.47, 0, 0.75, 0.72)" },
63 { name: "In · Quadratic", value: "cubic-bezier(0.55, 0.09, 0.68, 0.53)" },
64 { name: "In · Cubic", value: "cubic-bezier(0.55, 0.06, 0.68, 0.19)" },
65 { name: "In · Back", value: "cubic-bezier(0.6, -0.28, 0.74, 0.05)" }
68 { name: "ease-out", value: "ease-out" },
69 { name: "Out · Sine", value: "cubic-bezier(0.39, 0.58, 0.57, 1)" },
70 { name: "Out · Quadratic", value: "cubic-bezier(0.25, 0.46, 0.45, 0.94)" },
71 { name: "Out · Cubic", value: "cubic-bezier(0.22, 0.61, 0.36, 1)" },
72 { name: "Linear Out, Slow In", value: "cubic-bezier(0, 0, 0.2, 1)" },
73 { name: "Out · Back", value: "cubic-bezier(0.18, 0.89, 0.32, 1.28)" }
77 /** @typedef {{presets: !Array.<{name: string, value: string}>, icon: !Element, presetIndex: number}} */
78 WebInspector.BezierEditor.PresetCategory;
80 WebInspector.BezierEditor.prototype = {
81 /**
82 * @param {?WebInspector.Geometry.CubicBezier} bezier
84 setBezier: function(bezier)
86 if (!bezier)
87 return;
88 this._bezier = bezier;
89 this._updateUI();
92 /**
93 * @return {!WebInspector.Geometry.CubicBezier}
95 bezier: function()
97 return this._bezier;
100 wasShown: function()
102 this._unselectPresets();
103 // Check if bezier matches a preset
104 for (var category of this._presetCategories) {
105 for (var i = 0; i < category.presets.length; i++) {
106 if (this._bezier.asCSSText() === category.presets[i].value) {
107 category.presetIndex = i;
108 this._presetCategorySelected(category);
113 this._updateUI();
114 this._startPreviewAnimation();
117 _onchange: function()
119 this._updateUI();
120 this.dispatchEventToListeners(WebInspector.BezierEditor.Events.BezierChanged, this._bezier.asCSSText());
123 _updateUI: function()
125 var labelText = this._selectedCategory ? this._selectedCategory.presets[this._selectedCategory.presetIndex].name : this._bezier.asCSSText().replace(/\s(-\d\.\d)/g, "$1");
126 this._label.textContent = WebInspector.UIString(labelText);
127 this._curveUI.drawCurve(this._bezier, this._curve);
128 this._previewOnion.removeChildren();
132 * @param {!Event} event
133 * @return {boolean}
135 _dragStart: function(event)
137 this._mouseDownPosition = new WebInspector.Geometry.Point(event.x, event.y);
138 var ui = this._curveUI;
139 this._controlPosition = new WebInspector.Geometry.Point(
140 Number.constrain((event.offsetX - ui.radius) / ui.curveWidth(), 0, 1),
141 (ui.curveHeight() + ui.marginTop + ui.radius - event.offsetY) / ui.curveHeight());
143 var firstControlPointIsCloser = this._controlPosition.distanceTo(this._bezier.controlPoints[0]) < this._controlPosition.distanceTo(this._bezier.controlPoints[1]);
144 this._selectedPoint = firstControlPointIsCloser ? 0 : 1;
146 this._bezier.controlPoints[this._selectedPoint] = this._controlPosition;
147 this._unselectPresets();
148 this._onchange();
150 event.consume(true);
151 return true;
155 * @param {number} mouseX
156 * @param {number} mouseY
158 _updateControlPosition: function(mouseX, mouseY)
160 var deltaX = (mouseX - this._mouseDownPosition.x) / this._curveUI.curveWidth();
161 var deltaY = (mouseY - this._mouseDownPosition.y) / this._curveUI.curveHeight();
162 var newPosition = new WebInspector.Geometry.Point(Number.constrain(this._controlPosition.x + deltaX, 0, 1), this._controlPosition.y - deltaY);
163 this._bezier.controlPoints[this._selectedPoint] = newPosition;
167 * @param {!Event} event
169 _dragMove: function(event)
171 this._updateControlPosition(event.x, event.y);
172 this._onchange();
176 * @param {!Event} event
178 _dragEnd: function(event)
180 this._updateControlPosition(event.x, event.y);
181 this._onchange();
182 this._startPreviewAnimation();
186 * @param {!Array<{name: string, value: string}>} presetGroup
187 * @return {!WebInspector.BezierEditor.PresetCategory}
189 _createCategory: function(presetGroup)
191 var presetElement = createElementWithClass("div", "bezier-preset-category");
192 var iconElement = presetElement.createSVGChild("svg", "bezier-preset monospace");
193 var category = { presets: presetGroup, presetIndex: 0, icon: presetElement };
194 this._presetUI.drawCurve(WebInspector.Geometry.CubicBezier.parse(category.presets[0].value), iconElement);
195 iconElement.addEventListener("click", this._presetCategorySelected.bind(this, category));
196 return category;
200 * @param {!Element} parentElement
201 * @param {string} className
202 * @param {string} drawPath
203 * @return {!Element}
205 _createPresetModifyIcon: function (parentElement, className, drawPath)
207 var icon = parentElement.createSVGChild("svg", "bezier-preset-modify " + className);
208 icon.setAttribute("width", 20);
209 icon.setAttribute("height", 20);
210 var path = icon.createSVGChild("path");
211 path.setAttribute("d", drawPath);
212 return icon;
215 _unselectPresets: function()
217 for (var category of this._presetCategories)
218 category.icon.classList.remove("bezier-preset-selected");
219 delete this._selectedCategory;
220 this._header.classList.remove("bezier-header-active");
224 * @param {!WebInspector.BezierEditor.PresetCategory} category
225 * @param {!Event=} event
227 _presetCategorySelected: function(category, event)
229 if (this._selectedCategory === category)
230 return;
231 this._unselectPresets();
232 this._header.classList.add("bezier-header-active");
233 this._selectedCategory = category;
234 this._selectedCategory.icon.classList.add("bezier-preset-selected");
235 this.setBezier(WebInspector.Geometry.CubicBezier.parse(category.presets[category.presetIndex].value));
236 this._onchange();
237 this._startPreviewAnimation();
238 if (event)
239 event.consume(true);
243 * @param {boolean} intensify
244 * @param {!Event} event
246 _presetModifyClicked: function(intensify, event)
248 if (!this._selectedCategory)
249 return;
251 var length = this._selectedCategory.presets.length;
252 this._selectedCategory.presetIndex = (this._selectedCategory.presetIndex + (intensify ? 1 : -1) + length) % length;
253 this.setBezier(WebInspector.Geometry.CubicBezier.parse(this._selectedCategory.presets[this._selectedCategory.presetIndex].value));
254 this._onchange();
255 this._startPreviewAnimation();
258 _startPreviewAnimation: function()
260 if (this._previewAnimation)
261 this._previewAnimation.cancel();
263 const animationDuration = 1600;
264 const numberOnionSlices = 20;
266 var keyframes = [{ offset: 0, transform: "translateX(0px)", easing: this._bezier.asCSSText(), opacity: 1 },
267 { offset: 0.9, transform: "translateX(218px)", opacity: 1 },
268 { offset: 1, transform: "translateX(218px)", opacity: 0 }];
269 this._previewAnimation = this._previewElement.animate(keyframes, animationDuration);
270 this._previewOnion.removeChildren();
271 for (var i = 0; i <= numberOnionSlices; i++) {
272 var slice = this._previewOnion.createChild("div", "bezier-preview-animation");
273 var player = slice.animate([{ transform: "translateX(0px)", easing: this._bezier.asCSSText() }, { transform: "translateX(218px)" }],
274 { duration: animationDuration, fill: "forwards" });
275 player.pause();
276 player.currentTime = animationDuration * i / numberOnionSlices;
280 __proto__: WebInspector.VBox.prototype