UNPKG

9.32 kBJavaScriptView Raw
1// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15(function(scope, testing) {
16
17 // This returns a function for converting transform functions to equivalent
18 // primitive functions, which will take an array of values from the
19 // derivative type and fill in the blanks (underscores) with them.
20 var _ = null;
21 function cast(pattern) {
22 return function(contents) {
23 var i = 0;
24 return pattern.map(function(x) { return x === _ ? contents[i++] : x; });
25 }
26 }
27
28 function id(x) { return x; }
29
30 var Opx = {px: 0};
31 var Odeg = {deg: 0};
32
33 // type: [argTypes, convertTo3D, convertTo2D]
34 // In the argument types string, lowercase characters represent optional arguments
35 var transformFunctions = {
36 matrix: ['NNNNNN', [_, _, 0, 0, _, _, 0, 0, 0, 0, 1, 0, _, _, 0, 1], id],
37 matrix3d: ['NNNNNNNNNNNNNNNN', id],
38 rotate: ['A'],
39 rotatex: ['A'],
40 rotatey: ['A'],
41 rotatez: ['A'],
42 rotate3d: ['NNNA'],
43 perspective: ['L'],
44 scale: ['Nn', cast([_, _, 1]), id],
45 scalex: ['N', cast([_, 1, 1]), cast([_, 1])],
46 scaley: ['N', cast([1, _, 1]), cast([1, _])],
47 scalez: ['N', cast([1, 1, _])],
48 scale3d: ['NNN', id],
49 skew: ['Aa', null, id],
50 skewx: ['A', null, cast([_, Odeg])],
51 skewy: ['A', null, cast([Odeg, _])],
52 translate: ['Tt', cast([_, _, Opx]), id],
53 translatex: ['T', cast([_, Opx, Opx]), cast([_, Opx])],
54 translatey: ['T', cast([Opx, _, Opx]), cast([Opx, _])],
55 translatez: ['L', cast([Opx, Opx, _])],
56 translate3d: ['TTL', id],
57 };
58
59 function parseTransform(string) {
60 string = string.toLowerCase().trim();
61 if (string == 'none')
62 return [];
63 // FIXME: Using a RegExp means calcs won't work here
64 var transformRegExp = /\s*(\w+)\(([^)]*)\)/g;
65 var result = [];
66 var match;
67 var prevLastIndex = 0;
68 while (match = transformRegExp.exec(string)) {
69 if (match.index != prevLastIndex)
70 return;
71 prevLastIndex = match.index + match[0].length;
72 var functionName = match[1];
73 var functionData = transformFunctions[functionName];
74 if (!functionData)
75 return;
76 var args = match[2].split(',');
77 var argTypes = functionData[0];
78 if (argTypes.length < args.length)
79 return;
80
81 var parsedArgs = [];
82 for (var i = 0; i < argTypes.length; i++) {
83 var arg = args[i];
84 var type = argTypes[i];
85 var parsedArg;
86 if (!arg)
87 parsedArg = ({a: Odeg,
88 n: parsedArgs[0],
89 t: Opx})[type];
90 else
91 parsedArg = ({A: function(s) { return s.trim() == '0' ? Odeg : scope.parseAngle(s); },
92 N: scope.parseNumber,
93 T: scope.parseLengthOrPercent,
94 L: scope.parseLength})[type.toUpperCase()](arg);
95 if (parsedArg === undefined)
96 return;
97 parsedArgs.push(parsedArg);
98 }
99 result.push({t: functionName, d: parsedArgs});
100
101 if (transformRegExp.lastIndex == string.length)
102 return result;
103 }
104 };
105
106 function numberToLongString(x) {
107 return x.toFixed(6).replace('.000000', '');
108 }
109
110 function mergeMatrices(left, right) {
111 if (left.decompositionPair !== right) {
112 left.decompositionPair = right;
113 var leftArgs = scope.makeMatrixDecomposition(left);
114 }
115 if (right.decompositionPair !== left) {
116 right.decompositionPair = left;
117 var rightArgs = scope.makeMatrixDecomposition(right);
118 }
119 if (leftArgs[0] == null || rightArgs[0] == null)
120 return [[false], [true], function(x) { return x ? right[0].d : left[0].d; }];
121 leftArgs[0].push(0);
122 rightArgs[0].push(1);
123 return [
124 leftArgs,
125 rightArgs,
126 function(list) {
127 var quat = scope.quat(leftArgs[0][3], rightArgs[0][3], list[5]);
128 var mat = scope.composeMatrix(list[0], list[1], list[2], quat, list[4]);
129 var stringifiedArgs = mat.map(numberToLongString).join(',');
130 return stringifiedArgs;
131 }
132 ];
133 }
134
135 function typeTo2D(type) {
136 return type.replace(/[xy]/, '');
137 }
138
139 function typeTo3D(type) {
140 return type.replace(/(x|y|z|3d)?$/, '3d');
141 }
142
143 function mergeTransforms(left, right) {
144 var matrixModulesLoaded = scope.makeMatrixDecomposition && true;
145
146 var flipResults = false;
147 if (!left.length || !right.length) {
148 if (!left.length) {
149 flipResults = true;
150 left = right;
151 right = [];
152 }
153 for (var i = 0; i < left.length; i++) {
154 var type = left[i].t;
155 var args = left[i].d;
156 var defaultValue = type.substr(0, 5) == 'scale' ? 1 : 0;
157 right.push({t: type, d: args.map(function(arg) {
158 if (typeof arg == 'number')
159 return defaultValue;
160 var result = {};
161 for (var unit in arg)
162 result[unit] = defaultValue;
163 return result;
164 })});
165 }
166 }
167
168 var isMatrixOrPerspective = function(lt, rt) {
169 return ((lt == 'perspective') && (rt == 'perspective')) ||
170 ((lt == 'matrix' || lt == 'matrix3d') && (rt == 'matrix' || rt == 'matrix3d'));
171 };
172 var leftResult = [];
173 var rightResult = [];
174 var types = [];
175
176 if (left.length != right.length) {
177 if (!matrixModulesLoaded)
178 return;
179 var merged = mergeMatrices(left, right);
180 leftResult = [merged[0]];
181 rightResult = [merged[1]];
182 types = [['matrix', [merged[2]]]];
183 } else {
184 for (var i = 0; i < left.length; i++) {
185 var leftType = left[i].t;
186 var rightType = right[i].t;
187 var leftArgs = left[i].d;
188 var rightArgs = right[i].d;
189
190 var leftFunctionData = transformFunctions[leftType];
191 var rightFunctionData = transformFunctions[rightType];
192
193 var type;
194 if (isMatrixOrPerspective(leftType, rightType)) {
195 if (!matrixModulesLoaded)
196 return;
197 var merged = mergeMatrices([left[i]], [right[i]]);
198 leftResult.push(merged[0]);
199 rightResult.push(merged[1]);
200 types.push(['matrix', [merged[2]]]);
201 continue;
202 } else if (leftType == rightType) {
203 type = leftType;
204 } else if (leftFunctionData[2] && rightFunctionData[2] && typeTo2D(leftType) == typeTo2D(rightType)) {
205 type = typeTo2D(leftType);
206 leftArgs = leftFunctionData[2](leftArgs);
207 rightArgs = rightFunctionData[2](rightArgs);
208 } else if (leftFunctionData[1] && rightFunctionData[1] && typeTo3D(leftType) == typeTo3D(rightType)) {
209 type = typeTo3D(leftType);
210 leftArgs = leftFunctionData[1](leftArgs);
211 rightArgs = rightFunctionData[1](rightArgs);
212 } else {
213 if (!matrixModulesLoaded)
214 return;
215 var merged = mergeMatrices(left, right);
216 leftResult = [merged[0]];
217 rightResult = [merged[1]];
218 types = [['matrix', [merged[2]]]];
219 break;
220 }
221
222 var leftArgsCopy = [];
223 var rightArgsCopy = [];
224 var stringConversions = [];
225 for (var j = 0; j < leftArgs.length; j++) {
226 var merge = typeof leftArgs[j] == 'number' ? scope.mergeNumbers : scope.mergeDimensions;
227 var merged = merge(leftArgs[j], rightArgs[j]);
228 leftArgsCopy[j] = merged[0];
229 rightArgsCopy[j] = merged[1];
230 stringConversions.push(merged[2]);
231 }
232 leftResult.push(leftArgsCopy);
233 rightResult.push(rightArgsCopy);
234 types.push([type, stringConversions]);
235 }
236 }
237
238 if (flipResults) {
239 var tmp = leftResult;
240 leftResult = rightResult;
241 rightResult = tmp;
242 }
243
244 return [leftResult, rightResult, function(list) {
245 return list.map(function(args, i) {
246 var stringifiedArgs = args.map(function(arg, j) {
247 return types[i][1][j](arg);
248 }).join(',');
249 if (types[i][0] == 'matrix' && stringifiedArgs.split(',').length == 16)
250 types[i][0] = 'matrix3d';
251 return types[i][0] + '(' + stringifiedArgs + ')';
252
253 }).join(' ');
254 }];
255 }
256
257 scope.addPropertiesHandler(parseTransform, mergeTransforms, ['transform']);
258
259 scope.transformToSvgMatrix = function(string) {
260 // matrix(<a> <b> <c> <d> <e> <f>)
261 var mat = scope.transformListToMatrix(parseTransform(string));
262 return 'matrix(' +
263 numberToLongString(mat[0]) + ' ' + // <a>
264 numberToLongString(mat[1]) + ' ' + // <b>
265 numberToLongString(mat[4]) + ' ' + // <c>
266 numberToLongString(mat[5]) + ' ' + // <d>
267 numberToLongString(mat[12]) + ' ' + // <e>
268 numberToLongString(mat[13]) + // <f>
269 ')';
270 };
271
272 if (WEB_ANIMATIONS_TESTING)
273 testing.parseTransform = parseTransform;
274
275})(webAnimations1, webAnimationsTesting);