| 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 |