OLD | NEW |
| (Empty) |
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. | |
4 /** | |
5 * @unrestricted | |
6 */ | |
7 UI.BezierEditor = class extends UI.VBox { | |
8 constructor() { | |
9 super(true); | |
10 this.registerRequiredCSS('ui/bezierEditor.css'); | |
11 this.contentElement.tabIndex = 0; | |
12 this.setDefaultFocusedElement(this.contentElement); | |
13 | |
14 // Preview UI | |
15 this._previewElement = this.contentElement.createChild('div', 'bezier-previe
w-container'); | |
16 this._previewElement.createChild('div', 'bezier-preview-animation'); | |
17 this._previewElement.addEventListener('click', this._startPreviewAnimation.b
ind(this)); | |
18 this._previewOnion = this.contentElement.createChild('div', 'bezier-preview-
onion'); | |
19 this._previewOnion.addEventListener('click', this._startPreviewAnimation.bin
d(this)); | |
20 | |
21 this._outerContainer = this.contentElement.createChild('div', 'bezier-contai
ner'); | |
22 | |
23 // Presets UI | |
24 this._presetsContainer = this._outerContainer.createChild('div', 'bezier-pre
sets'); | |
25 this._presetUI = new UI.BezierUI(40, 40, 0, 2, false); | |
26 this._presetCategories = []; | |
27 for (var i = 0; i < UI.BezierEditor.Presets.length; i++) { | |
28 this._presetCategories[i] = this._createCategory(UI.BezierEditor.Presets[i
]); | |
29 this._presetsContainer.appendChild(this._presetCategories[i].icon); | |
30 } | |
31 | |
32 // Curve UI | |
33 this._curveUI = new UI.BezierUI(150, 250, 50, 7, true); | |
34 this._curve = this._outerContainer.createSVGChild('svg', 'bezier-curve'); | |
35 UI.installDragHandle( | |
36 this._curve, this._dragStart.bind(this), this._dragMove.bind(this), this
._dragEnd.bind(this), 'default'); | |
37 | |
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-v
alue'); | |
44 } | |
45 | |
46 /** | |
47 * @param {?Common.Geometry.CubicBezier} bezier | |
48 */ | |
49 setBezier(bezier) { | |
50 if (!bezier) | |
51 return; | |
52 this._bezier = bezier; | |
53 this._updateUI(); | |
54 } | |
55 | |
56 /** | |
57 * @return {!Common.Geometry.CubicBezier} | |
58 */ | |
59 bezier() { | |
60 return this._bezier; | |
61 } | |
62 | |
63 /** | |
64 * @override | |
65 */ | |
66 wasShown() { | |
67 this._unselectPresets(); | |
68 // Check if bezier matches a preset | |
69 for (var category of this._presetCategories) { | |
70 for (var i = 0; i < category.presets.length; i++) { | |
71 if (this._bezier.asCSSText() === category.presets[i].value) { | |
72 category.presetIndex = i; | |
73 this._presetCategorySelected(category); | |
74 } | |
75 } | |
76 } | |
77 | |
78 this._updateUI(); | |
79 this._startPreviewAnimation(); | |
80 } | |
81 | |
82 _onchange() { | |
83 this._updateUI(); | |
84 this.dispatchEventToListeners(UI.BezierEditor.Events.BezierChanged, this._be
zier.asCSSText()); | |
85 } | |
86 | |
87 _updateUI() { | |
88 var labelText = this._selectedCategory ? this._selectedCategory.presets[this
._selectedCategory.presetIndex].name : | |
89 this._bezier.asCSSText().replace(/\
s(-\d\.\d)/g, '$1'); | |
90 this._label.textContent = Common.UIString(labelText); | |
91 this._curveUI.drawCurve(this._bezier, this._curve); | |
92 this._previewOnion.removeChildren(); | |
93 } | |
94 | |
95 /** | |
96 * @param {!Event} event | |
97 * @return {boolean} | |
98 */ | |
99 _dragStart(event) { | |
100 this._mouseDownPosition = new Common.Geometry.Point(event.x, event.y); | |
101 var ui = this._curveUI; | |
102 this._controlPosition = new Common.Geometry.Point( | |
103 Number.constrain((event.offsetX - ui.radius) / ui.curveWidth(), 0, 1), | |
104 (ui.curveHeight() + ui.marginTop + ui.radius - event.offsetY) / ui.curve
Height()); | |
105 | |
106 var firstControlPointIsCloser = this._controlPosition.distanceTo(this._bezie
r.controlPoints[0]) < | |
107 this._controlPosition.distanceTo(this._bezier.controlPoints[1]); | |
108 this._selectedPoint = firstControlPointIsCloser ? 0 : 1; | |
109 | |
110 this._bezier.controlPoints[this._selectedPoint] = this._controlPosition; | |
111 this._unselectPresets(); | |
112 this._onchange(); | |
113 | |
114 event.consume(true); | |
115 return true; | |
116 } | |
117 | |
118 /** | |
119 * @param {number} mouseX | |
120 * @param {number} mouseY | |
121 */ | |
122 _updateControlPosition(mouseX, mouseY) { | |
123 var deltaX = (mouseX - this._mouseDownPosition.x) / this._curveUI.curveWidth
(); | |
124 var deltaY = (mouseY - this._mouseDownPosition.y) / this._curveUI.curveHeigh
t(); | |
125 var newPosition = new Common.Geometry.Point( | |
126 Number.constrain(this._controlPosition.x + deltaX, 0, 1), this._controlP
osition.y - deltaY); | |
127 this._bezier.controlPoints[this._selectedPoint] = newPosition; | |
128 } | |
129 | |
130 /** | |
131 * @param {!Event} event | |
132 */ | |
133 _dragMove(event) { | |
134 this._updateControlPosition(event.x, event.y); | |
135 this._onchange(); | |
136 } | |
137 | |
138 /** | |
139 * @param {!Event} event | |
140 */ | |
141 _dragEnd(event) { | |
142 this._updateControlPosition(event.x, event.y); | |
143 this._onchange(); | |
144 this._startPreviewAnimation(); | |
145 } | |
146 | |
147 /** | |
148 * @param {!Array<{name: string, value: string}>} presetGroup | |
149 * @return {!UI.BezierEditor.PresetCategory} | |
150 */ | |
151 _createCategory(presetGroup) { | |
152 var presetElement = createElementWithClass('div', 'bezier-preset-category'); | |
153 var iconElement = presetElement.createSVGChild('svg', 'bezier-preset monospa
ce'); | |
154 var category = {presets: presetGroup, presetIndex: 0, icon: presetElement}; | |
155 this._presetUI.drawCurve(Common.Geometry.CubicBezier.parse(category.presets[
0].value), iconElement); | |
156 iconElement.addEventListener('click', this._presetCategorySelected.bind(this
, category)); | |
157 return category; | |
158 } | |
159 | |
160 /** | |
161 * @param {!Element} parentElement | |
162 * @param {string} className | |
163 * @param {string} drawPath | |
164 * @return {!Element} | |
165 */ | |
166 _createPresetModifyIcon(parentElement, className, drawPath) { | |
167 var icon = parentElement.createSVGChild('svg', 'bezier-preset-modify ' + cla
ssName); | |
168 icon.setAttribute('width', 20); | |
169 icon.setAttribute('height', 20); | |
170 var path = icon.createSVGChild('path'); | |
171 path.setAttribute('d', drawPath); | |
172 return icon; | |
173 } | |
174 | |
175 _unselectPresets() { | |
176 for (var category of this._presetCategories) | |
177 category.icon.classList.remove('bezier-preset-selected'); | |
178 delete this._selectedCategory; | |
179 this._header.classList.remove('bezier-header-active'); | |
180 } | |
181 | |
182 /** | |
183 * @param {!UI.BezierEditor.PresetCategory} category | |
184 * @param {!Event=} event | |
185 */ | |
186 _presetCategorySelected(category, event) { | |
187 if (this._selectedCategory === category) | |
188 return; | |
189 this._unselectPresets(); | |
190 this._header.classList.add('bezier-header-active'); | |
191 this._selectedCategory = category; | |
192 this._selectedCategory.icon.classList.add('bezier-preset-selected'); | |
193 this.setBezier(Common.Geometry.CubicBezier.parse(category.presets[category.p
resetIndex].value)); | |
194 this._onchange(); | |
195 this._startPreviewAnimation(); | |
196 if (event) | |
197 event.consume(true); | |
198 } | |
199 | |
200 /** | |
201 * @param {boolean} intensify | |
202 * @param {!Event} event | |
203 */ | |
204 _presetModifyClicked(intensify, event) { | |
205 if (!this._selectedCategory) | |
206 return; | |
207 | |
208 var length = this._selectedCategory.presets.length; | |
209 this._selectedCategory.presetIndex = (this._selectedCategory.presetIndex + (
intensify ? 1 : -1) + length) % length; | |
210 this.setBezier( | |
211 Common.Geometry.CubicBezier.parse(this._selectedCategory.presets[this._s
electedCategory.presetIndex].value)); | |
212 this._onchange(); | |
213 this._startPreviewAnimation(); | |
214 } | |
215 | |
216 _startPreviewAnimation() { | |
217 if (this._previewAnimation) | |
218 this._previewAnimation.cancel(); | |
219 | |
220 const animationDuration = 1600; | |
221 const numberOnionSlices = 20; | |
222 | |
223 var keyframes = [ | |
224 {offset: 0, transform: 'translateX(0px)', easing: this._bezier.asCSSText()
, opacity: 1}, | |
225 {offset: 0.9, transform: 'translateX(218px)', opacity: 1}, | |
226 {offset: 1, transform: 'translateX(218px)', opacity: 0} | |
227 ]; | |
228 this._previewAnimation = this._previewElement.animate(keyframes, animationDu
ration); | |
229 this._previewOnion.removeChildren(); | |
230 for (var i = 0; i <= numberOnionSlices; i++) { | |
231 var slice = this._previewOnion.createChild('div', 'bezier-preview-animatio
n'); | |
232 var player = slice.animate( | |
233 [{transform: 'translateX(0px)', easing: this._bezier.asCSSText()}, {tr
ansform: 'translateX(218px)'}], | |
234 {duration: animationDuration, fill: 'forwards'}); | |
235 player.pause(); | |
236 player.currentTime = animationDuration * i / numberOnionSlices; | |
237 } | |
238 } | |
239 }; | |
240 | |
241 /** @enum {symbol} */ | |
242 UI.BezierEditor.Events = { | |
243 BezierChanged: Symbol('BezierChanged') | |
244 }; | |
245 | |
246 UI.BezierEditor.Presets = [ | |
247 [ | |
248 {name: 'ease-in-out', value: 'ease-in-out'}, {name: 'In Out · Sine', value:
'cubic-bezier(0.45, 0.05, 0.55, 0.95)'}, | |
249 {name: 'In Out · Quadratic', value: 'cubic-bezier(0.46, 0.03, 0.52, 0.96)'}, | |
250 {name: 'In Out · Cubic', value: 'cubic-bezier(0.65, 0.05, 0.36, 1)'}, | |
251 {name: 'Fast Out, Slow In', value: 'cubic-bezier(0.4, 0, 0.2, 1)'}, | |
252 {name: 'In Out · Back', value: 'cubic-bezier(0.68, -0.55, 0.27, 1.55)'} | |
253 ], | |
254 [ | |
255 {name: 'Fast Out, Linear In', value: 'cubic-bezier(0.4, 0, 1, 1)'}, {name: '
ease-in', value: 'ease-in'}, | |
256 {name: 'In · Sine', value: 'cubic-bezier(0.47, 0, 0.75, 0.72)'}, | |
257 {name: 'In · Quadratic', value: 'cubic-bezier(0.55, 0.09, 0.68, 0.53)'}, | |
258 {name: 'In · Cubic', value: 'cubic-bezier(0.55, 0.06, 0.68, 0.19)'}, | |
259 {name: 'In · Back', value: 'cubic-bezier(0.6, -0.28, 0.74, 0.05)'} | |
260 ], | |
261 [ | |
262 {name: 'ease-out', value: 'ease-out'}, {name: 'Out · Sine', value: 'cubic-be
zier(0.39, 0.58, 0.57, 1)'}, | |
263 {name: 'Out · Quadratic', value: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'}, | |
264 {name: 'Out · Cubic', value: 'cubic-bezier(0.22, 0.61, 0.36, 1)'}, | |
265 {name: 'Linear Out, Slow In', value: 'cubic-bezier(0, 0, 0.2, 1)'}, | |
266 {name: 'Out · Back', value: 'cubic-bezier(0.18, 0.89, 0.32, 1.28)'} | |
267 ] | |
268 ]; | |
269 | |
270 /** @typedef {{presets: !Array.<{name: string, value: string}>, icon: !Element,
presetIndex: number}} */ | |
271 UI.BezierEditor.PresetCategory; | |
OLD | NEW |