├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── alloy_crop └── README.md ├── alloy_finger.js ├── asset ├── a.html ├── alloy_paper.js ├── cover.jpg ├── image_loaded.js ├── loading.gif ├── test.png ├── test2.png ├── test3.png ├── to.js ├── transform.js └── yes.png ├── example ├── canvas │ └── index.html ├── destroy │ └── index.html ├── on_off │ └── index.html ├── picture │ └── index.html ├── simple │ └── index.html ├── tap_state │ └── index.html └── test │ └── index.html ├── index.html ├── package.json ├── react └── AlloyFinger.jsx └── transformjs ├── README.md └── transform.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "browsers": ["last 2 versions", "> 5%"], 6 | "ios": 7, 7 | "android": 4, 8 | "node": 6, 9 | "ie": 11 10 | }, 11 | "include": [], 12 | "exclude": [ 13 | "transform-async-to-generator" 14 | ], 15 | "uglify": true, 16 | "useBuiltIns": true, 17 | "loose": false, 18 | "debug": false 19 | }], 20 | "react" 21 | ], 22 | "plugins": [ 23 | "add-module-exports", 24 | "transform-es2015-modules-commonjs", 25 | "fast-async", 26 | "transform-function-bind", 27 | "transform-class-properties", 28 | "transform-object-rest-spread", 29 | "transform-decorators" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | dist/ 3 | .DS_Store 4 | npm-debug.log 5 | .idea/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 腾讯 AlloyTeam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Preview 2 | 3 | You can touch this → [http://alloyteam.github.io/AlloyFinger/](http://alloyteam.github.io/AlloyFinger/) 4 | 5 | # Install 6 | 7 | You can install it via npm: 8 | 9 | ```html 10 | npm install alloyfinger 11 | ``` 12 | 13 | # Usage 14 | 15 | ```js 16 | var af = new AlloyFinger(element, { 17 | touchStart: function () { }, 18 | touchMove: function () { }, 19 | touchEnd: function () { }, 20 | touchCancel: function () { }, 21 | multipointStart: function () { }, 22 | multipointEnd: function () { }, 23 | tap: function () { }, 24 | doubleTap: function () { }, 25 | longTap: function () { }, 26 | singleTap: function () { }, 27 | rotate: function (evt) { 28 | console.log(evt.angle); 29 | }, 30 | pinch: function (evt) { 31 | console.log(evt.zoom); 32 | }, 33 | pressMove: function (evt) { 34 | console.log(evt.deltaX); 35 | console.log(evt.deltaY); 36 | }, 37 | swipe: function (evt) { 38 | console.log("swipe" + evt.direction); 39 | } 40 | }); 41 | 42 | /** 43 | * this method can also add or remove the event handler 44 | */ 45 | var onTap = function() {}; 46 | 47 | af.on('tap', onTap); 48 | af.on('touchStart', function() {}); 49 | 50 | af.off('tap', onTap); 51 | 52 | /** 53 | * this method can destroy the instance 54 | */ 55 | af = af.destroy(); 56 | ``` 57 | 58 | ### Omi Version: 59 | 60 | 61 | ```js 62 | import { render, tag, WeElement } from 'omi' 63 | import 'omi-finger' 64 | 65 | @tag('my-app') 66 | class MyApp extends WeElement { 67 | install() { 68 | this.data.wording = 'Tap or Swipe Me!' 69 | } 70 | 71 | handleTap = (evt) => { 72 | this.data.wording += '\r\nTap' 73 | this.update() 74 | } 75 | 76 | handleSwipe = (evt) => { 77 | this.data.wording += '\r\nSwipe-' + evt.direction 78 | this.update() 79 | } 80 | 81 | render() { 82 | return ( 83 |
84 | 85 |
86 | {this.data.wording} 87 |
88 |
89 |
90 | ) 91 | } 92 | 93 | css() { 94 | return `.touchArea{ 95 | background-color: green; 96 | width: 200px; 97 | min-height: 200px; 98 | text-align: center; 99 | color:white; 100 | height:auto; 101 | white-space: pre-line; 102 | }` 103 | } 104 | } 105 | 106 | render(, 'body') 107 | ``` 108 | 109 | * [omi-finger](https://github.com/Tencent/omi/tree/master/packages/omi-finger) 110 | * [css3transform](https://github.com/Tencent/omi/tree/master/packages/omi-transform) 111 | 112 | # Others 113 | 114 | * [AlloyCrop](https://github.com/AlloyTeam/AlloyCrop) 115 | 116 | 117 | # License 118 | This content is released under the [MIT](http://opensource.org/licenses/MIT) License. 119 | -------------------------------------------------------------------------------- /alloy_crop/README.md: -------------------------------------------------------------------------------- 1 | # 项目新地址 2 | 3 | New Address -> [https://github.com/AlloyTeam/AlloyCrop](https://github.com/AlloyTeam/AlloyCrop) 4 | -------------------------------------------------------------------------------- /alloy_finger.js: -------------------------------------------------------------------------------- 1 | /* AlloyFinger v0.1.15 2 | * By dntzhang 3 | * Github: https://github.com/AlloyTeam/AlloyFinger 4 | */ 5 | ; (function () { 6 | function getLen(v) { 7 | return Math.sqrt(v.x * v.x + v.y * v.y); 8 | } 9 | 10 | function dot(v1, v2) { 11 | return v1.x * v2.x + v1.y * v2.y; 12 | } 13 | 14 | function getAngle(v1, v2) { 15 | var mr = getLen(v1) * getLen(v2); 16 | if (mr === 0) return 0; 17 | var r = dot(v1, v2) / mr; 18 | if (r > 1) r = 1; 19 | return Math.acos(r); 20 | } 21 | 22 | function cross(v1, v2) { 23 | return v1.x * v2.y - v2.x * v1.y; 24 | } 25 | 26 | function getRotateAngle(v1, v2) { 27 | var angle = getAngle(v1, v2); 28 | if (cross(v1, v2) > 0) { 29 | angle *= -1; 30 | } 31 | 32 | return angle * 180 / Math.PI; 33 | } 34 | 35 | var HandlerAdmin = function(el) { 36 | this.handlers = []; 37 | this.el = el; 38 | }; 39 | 40 | HandlerAdmin.prototype.add = function(handler) { 41 | this.handlers.push(handler); 42 | } 43 | 44 | HandlerAdmin.prototype.del = function(handler) { 45 | if(!handler) this.handlers = []; 46 | 47 | for(var i=this.handlers.length; i>=0; i--) { 48 | if(this.handlers[i] === handler) { 49 | this.handlers.splice(i, 1); 50 | } 51 | } 52 | } 53 | 54 | HandlerAdmin.prototype.dispatch = function() { 55 | for(var i=0,len=this.handlers.length; i 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30); 129 | if (this.isDoubleTap) clearTimeout(this.singleTapTimeout); 130 | } 131 | this.preTapPosition.x = this.x1; 132 | this.preTapPosition.y = this.y1; 133 | this.last = this.now; 134 | var preV = this.preV, 135 | len = evt.touches.length; 136 | if (len > 1) { 137 | this._cancelLongTap(); 138 | this._cancelSingleTap(); 139 | var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 }; 140 | preV.x = v.x; 141 | preV.y = v.y; 142 | this.pinchStartLen = getLen(preV); 143 | this.multipointStart.dispatch(evt, this.element); 144 | } 145 | this._preventTap = false; 146 | this.longTapTimeout = setTimeout(function () { 147 | this.longTap.dispatch(evt, this.element); 148 | this._preventTap = true; 149 | }.bind(this), 750); 150 | }, 151 | move: function (evt) { 152 | if (!evt.touches) return; 153 | var preV = this.preV, 154 | len = evt.touches.length, 155 | currentX = evt.touches[0].pageX, 156 | currentY = evt.touches[0].pageY; 157 | this.isDoubleTap = false; 158 | if (len > 1) { 159 | var sCurrentX = evt.touches[1].pageX, 160 | sCurrentY = evt.touches[1].pageY 161 | var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY }; 162 | 163 | if (preV.x !== null) { 164 | if (this.pinchStartLen > 0) { 165 | evt.zoom = getLen(v) / this.pinchStartLen; 166 | this.pinch.dispatch(evt, this.element); 167 | } 168 | 169 | evt.angle = getRotateAngle(v, preV); 170 | this.rotate.dispatch(evt, this.element); 171 | } 172 | preV.x = v.x; 173 | preV.y = v.y; 174 | 175 | if (this.x2 !== null && this.sx2 !== null) { 176 | evt.deltaX = (currentX - this.x2 + sCurrentX - this.sx2) / 2; 177 | evt.deltaY = (currentY - this.y2 + sCurrentY - this.sy2) / 2; 178 | } else { 179 | evt.deltaX = 0; 180 | evt.deltaY = 0; 181 | } 182 | this.twoFingerPressMove.dispatch(evt, this.element); 183 | 184 | this.sx2 = sCurrentX; 185 | this.sy2 = sCurrentY; 186 | } else { 187 | if (this.x2 !== null) { 188 | evt.deltaX = currentX - this.x2; 189 | evt.deltaY = currentY - this.y2; 190 | 191 | //move事件中添加对当前触摸点到初始触摸点的判断, 192 | //如果曾经大于过某个距离(比如10),就认为是移动到某个地方又移回来,应该不再触发tap事件才对。 193 | var movedX = Math.abs(this.x1 - this.x2), 194 | movedY = Math.abs(this.y1 - this.y2); 195 | 196 | if(movedX > 10 || movedY > 10){ 197 | this._preventTap = true; 198 | } 199 | 200 | } else { 201 | evt.deltaX = 0; 202 | evt.deltaY = 0; 203 | } 204 | 205 | 206 | this.pressMove.dispatch(evt, this.element); 207 | } 208 | 209 | this.touchMove.dispatch(evt, this.element); 210 | 211 | this._cancelLongTap(); 212 | this.x2 = currentX; 213 | this.y2 = currentY; 214 | 215 | if (len > 1) { 216 | evt.preventDefault(); 217 | } 218 | }, 219 | end: function (evt) { 220 | if (!evt.changedTouches) return; 221 | this._cancelLongTap(); 222 | var self = this; 223 | if (evt.touches.length < 2) { 224 | this.multipointEnd.dispatch(evt, this.element); 225 | this.sx2 = this.sy2 = null; 226 | } 227 | 228 | //swipe 229 | if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) || 230 | (this.y2 && Math.abs(this.y1 - this.y2) > 30)) { 231 | evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2); 232 | this.swipeTimeout = setTimeout(function () { 233 | self.swipe.dispatch(evt, self.element); 234 | 235 | }, 0) 236 | } else { 237 | this.tapTimeout = setTimeout(function () { 238 | if(!self._preventTap){ 239 | self.tap.dispatch(evt, self.element); 240 | } 241 | // trigger double tap immediately 242 | if (self.isDoubleTap) { 243 | self.doubleTap.dispatch(evt, self.element); 244 | self.isDoubleTap = false; 245 | } 246 | }, 0) 247 | 248 | if (!self.isDoubleTap) { 249 | self.singleTapTimeout = setTimeout(function () { 250 | self.singleTap.dispatch(evt, self.element); 251 | }, 250); 252 | } 253 | } 254 | 255 | this.touchEnd.dispatch(evt, this.element); 256 | 257 | this.preV.x = 0; 258 | this.preV.y = 0; 259 | this.zoom = 1; 260 | this.pinchStartLen = null; 261 | this.x1 = this.x2 = this.y1 = this.y2 = null; 262 | }, 263 | cancelAll: function () { 264 | this._preventTap = true 265 | clearTimeout(this.singleTapTimeout); 266 | clearTimeout(this.tapTimeout); 267 | clearTimeout(this.longTapTimeout); 268 | clearTimeout(this.swipeTimeout); 269 | }, 270 | cancel: function (evt) { 271 | this.cancelAll() 272 | this.touchCancel.dispatch(evt, this.element); 273 | }, 274 | _cancelLongTap: function () { 275 | clearTimeout(this.longTapTimeout); 276 | }, 277 | _cancelSingleTap: function () { 278 | clearTimeout(this.singleTapTimeout); 279 | }, 280 | _swipeDirection: function (x1, x2, y1, y2) { 281 | return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') 282 | }, 283 | 284 | on: function(evt, handler) { 285 | if(this[evt]) { 286 | this[evt].add(handler); 287 | } 288 | }, 289 | 290 | off: function(evt, handler) { 291 | if(this[evt]) { 292 | this[evt].del(handler); 293 | } 294 | }, 295 | 296 | destroy: function() { 297 | if(this.singleTapTimeout) clearTimeout(this.singleTapTimeout); 298 | if(this.tapTimeout) clearTimeout(this.tapTimeout); 299 | if(this.longTapTimeout) clearTimeout(this.longTapTimeout); 300 | if(this.swipeTimeout) clearTimeout(this.swipeTimeout); 301 | 302 | this.element.removeEventListener("touchstart", this.start); 303 | this.element.removeEventListener("touchmove", this.move); 304 | this.element.removeEventListener("touchend", this.end); 305 | this.element.removeEventListener("touchcancel", this.cancel); 306 | 307 | this.rotate.del(); 308 | this.touchStart.del(); 309 | this.multipointStart.del(); 310 | this.multipointEnd.del(); 311 | this.pinch.del(); 312 | this.swipe.del(); 313 | this.tap.del(); 314 | this.doubleTap.del(); 315 | this.longTap.del(); 316 | this.singleTap.del(); 317 | this.pressMove.del(); 318 | this.twoFingerPressMove.del() 319 | this.touchMove.del(); 320 | this.touchEnd.del(); 321 | this.touchCancel.del(); 322 | 323 | this.preV = this.pinchStartLen = this.zoom = this.isDoubleTap = this.delta = this.last = this.now = this.tapTimeout = this.singleTapTimeout = this.longTapTimeout = this.swipeTimeout = this.x1 = this.x2 = this.y1 = this.y2 = this.preTapPosition = this.rotate = this.touchStart = this.multipointStart = this.multipointEnd = this.pinch = this.swipe = this.tap = this.doubleTap = this.longTap = this.singleTap = this.pressMove = this.touchMove = this.touchEnd = this.touchCancel = this.twoFingerPressMove = null; 324 | 325 | window.removeEventListener('scroll', this._cancelAllHandler); 326 | return null; 327 | } 328 | }; 329 | 330 | if (typeof module !== 'undefined' && typeof exports === 'object') { 331 | module.exports = AlloyFinger; 332 | } else { 333 | window.AlloyFinger = AlloyFinger; 334 | } 335 | })(); 336 | -------------------------------------------------------------------------------- /asset/a.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |
13 | 14 |
15 | transformjs 16 |
17 | 18 |
19 | transformjs 20 |
21 | 22 |
23 | transformjs 24 |
25 | 26 |
27 | transformjs 28 |
29 | 30 |
31 | transformjs 32 |
33 | 34 |
35 | transformjs 36 |
37 | 38 |
39 | transformjs 40 |
41 | 42 |
43 | transformjs 44 |
45 | 46 |
47 | transformjs 48 |
49 |
50 | transformjs 51 |
52 |
53 | 54 | 105 | 106 | -------------------------------------------------------------------------------- /asset/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/cover.jpg -------------------------------------------------------------------------------- /asset/image_loaded.js: -------------------------------------------------------------------------------- 1 | function imageLoaded(selector,onload){ 2 | var img=new Image() ; 3 | var dom=document.querySelector(selector); 4 | img.onload=function(){ 5 | //real_width,real_height 6 | onload.call(dom,this.width,this.height); 7 | img.onload=null; 8 | img=null; 9 | }; 10 | img.src=dom.getAttribute("src"); 11 | } -------------------------------------------------------------------------------- /asset/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/loading.gif -------------------------------------------------------------------------------- /asset/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/test.png -------------------------------------------------------------------------------- /asset/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/test2.png -------------------------------------------------------------------------------- /asset/test3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/test3.png -------------------------------------------------------------------------------- /asset/to.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/to.js -------------------------------------------------------------------------------- /asset/transform.js: -------------------------------------------------------------------------------- 1 | /* transformjs 2 | * By dntzhang 3 | */ 4 | ;(function () { 5 | 6 | var Matrix3D = function (n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { 7 | this.elements =window.Float32Array ? new Float32Array(16) : []; 8 | var te = this.elements; 9 | te[0] = (n11 !== undefined) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0; 10 | te[1] = n21 || 0; te[5] = (n22 !== undefined) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0; 11 | te[2] = n31 || 0; te[6] = n32 || 0; te[10] = (n33 !== undefined) ? n33 : 1; te[14] = n34 || 0; 12 | te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = (n44 !== undefined) ? n44 : 1; 13 | }; 14 | 15 | Matrix3D.DEG_TO_RAD = Math.PI / 180; 16 | 17 | Matrix3D.prototype = { 18 | set: function (n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { 19 | var te = this.elements; 20 | te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; 21 | te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; 22 | te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; 23 | te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; 24 | return this; 25 | }, 26 | identity: function () { 27 | this.set( 28 | 1, 0, 0, 0, 29 | 0, 1, 0, 0, 30 | 0, 0, 1, 0, 31 | 0, 0, 0, 1 32 | ); 33 | return this; 34 | }, 35 | multiplyMatrices: function (a, be) { 36 | 37 | var ae = a.elements; 38 | var te = this.elements; 39 | var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; 40 | var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; 41 | var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; 42 | var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; 43 | 44 | var b11 = be[0], b12 = be[1], b13 = be[2], b14 = be[3]; 45 | var b21 = be[4], b22 = be[5], b23 = be[6], b24 = be[7]; 46 | var b31 = be[8], b32 = be[9], b33 = be[10], b34 = be[11]; 47 | var b41 = be[12], b42 = be[13], b43 = be[14], b44 = be[15]; 48 | 49 | te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 50 | te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 51 | te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 52 | te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 53 | 54 | te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 55 | te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 56 | te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 57 | te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 58 | 59 | te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 60 | te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 61 | te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 62 | te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 63 | 64 | te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 65 | te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 66 | te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 67 | te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 68 | 69 | return this; 70 | 71 | }, 72 | // 解决角度为90的整数倍导致Math.cos得到极小的数,其实是0。导致不渲染 73 | _rounded: function(value,i){ 74 | i= Math.pow(10, i || 15); 75 | // default 76 | return Math.round(value*i)/i; 77 | }, 78 | appendTransform: function (x, y, z, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ,skewX,skewY, originX, originY, originZ) { 79 | 80 | var rx = rotateX * Matrix3D.DEG_TO_RAD; 81 | var cosx =this._rounded( Math.cos(rx)); 82 | var sinx = this._rounded(Math.sin(rx)); 83 | var ry = rotateY * Matrix3D.DEG_TO_RAD; 84 | var cosy =this._rounded( Math.cos(ry)); 85 | var siny = this._rounded(Math.sin(ry)); 86 | var rz = rotateZ * Matrix3D.DEG_TO_RAD; 87 | var cosz =this._rounded( Math.cos(rz * -1)); 88 | var sinz =this._rounded( Math.sin(rz * -1)); 89 | 90 | this.multiplyMatrices(this, [ 91 | 1, 0, 0, x, 92 | 0, cosx, sinx, y, 93 | 0, -sinx, cosx, z, 94 | 0, 0, 0, 1 95 | ]); 96 | 97 | this.multiplyMatrices(this, [ 98 | cosy, 0, siny, 0, 99 | 0, 1, 0, 0, 100 | -siny, 0, cosy, 0, 101 | 0, 0, 0, 1 102 | ]); 103 | 104 | this.multiplyMatrices(this,[ 105 | cosz * scaleX, sinz * scaleY, 0, 0, 106 | -sinz * scaleX, cosz * scaleY, 0, 0, 107 | 0, 0, 1 * scaleZ, 0, 108 | 0, 0, 0, 1 109 | ]); 110 | 111 | if(skewX||skewY){ 112 | this.multiplyMatrices(this,[ 113 | this._rounded(Math.cos(skewX* Matrix3D.DEG_TO_RAD)), this._rounded( Math.sin(skewX* Matrix3D.DEG_TO_RAD)), 0, 0, 114 | -1*this._rounded(Math.sin(skewY* Matrix3D.DEG_TO_RAD)), this._rounded( Math.cos(skewY* Matrix3D.DEG_TO_RAD)), 0, 0, 115 | 0, 0, 1, 0, 116 | 0, 0, 0, 1 117 | ]); 118 | } 119 | 120 | if (originX || originY || originZ) { 121 | this.elements[12] -= originX * this.elements[0] + originY * this.elements[4] + originZ * this.elements[8]; 122 | this.elements[13] -= originX * this.elements[1] + originY * this.elements[5] + originZ * this.elements[9]; 123 | this.elements[14] -= originX * this.elements[2] + originY * this.elements[6] + originZ * this.elements[10]; 124 | } 125 | return this; 126 | } 127 | }; 128 | 129 | function observe(target, props, callback) { 130 | for (var i = 0, len = props.length; i < len; i++) { 131 | var prop = props[i]; 132 | watch(target, prop, callback); 133 | } 134 | } 135 | 136 | function watch(target, prop, callback) { 137 | Object.defineProperty(target, prop, { 138 | get: function () { 139 | return this["__" + prop]; 140 | }, 141 | set: function (value) { 142 | if (value !== this["__" + prop]) { 143 | this["__" + prop] = value; 144 | callback(); 145 | } 146 | 147 | } 148 | }); 149 | } 150 | 151 | window.Transform = function (element) { 152 | 153 | observe( 154 | element, 155 | ["translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ" , "rotateX", "rotateY", "rotateZ","skewX","skewY", "originX", "originY", "originZ"], 156 | function () { 157 | var mtx = element.matrix3D.identity().appendTransform( element.translateX, element.translateY, element.translateZ, element.scaleX, element.scaleY, element.scaleZ, element.rotateX, element.rotateY, element.rotateZ,element.skewX,element.skewY, element.originX, element.originY, element.originZ); 158 | element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = "perspective("+element.perspective+"px) matrix3d(" + Array.prototype.slice.call(mtx.elements).join(",") + ")"; 159 | }); 160 | 161 | observe( 162 | element, 163 | [ "perspective"], 164 | function () { 165 | element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = "perspective("+element.perspective+"px) matrix3d(" + Array.prototype.slice.call(element.matrix3D.elements).join(",") + ")"; 166 | }); 167 | 168 | element.matrix3D = new Matrix3D(); 169 | element.perspective = 500; 170 | element.scaleX = element.scaleY = element.scaleZ = 1; 171 | //由于image自带了x\y\z,所有加上translate前缀 172 | element.translateX = element.translateY = element.translateZ = element.rotateX = element.rotateY = element.rotateZ =element.skewX=element.skewY= element.originX = element.originY = element.originZ = 0; 173 | } 174 | })(); -------------------------------------------------------------------------------- /asset/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlloyTeam/AlloyFinger/38f2062e65d311db4d51457f06bc0ec77d66f664/asset/yes.png -------------------------------------------------------------------------------- /example/canvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Canvas+AlloyFinger 6 | 7 | 30 | 31 | 32 | Fork me on Github 33 | 34 | 35 |
36 | 37 | 116 | 117 | -------------------------------------------------------------------------------- /example/destroy/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Event Test 5 | 6 | 14 | 15 | 16 |
Touch Me
17 |
18 | 19 |
20 | 52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /example/on_off/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Event Test 5 | 6 | 14 | 15 | 16 |
Touch Me
17 |
18 | 19 |
20 | 58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /example/picture/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | AlloyFinger 5 | 6 | 29 | 30 | 31 | Fork me on Github 32 | 33 | 34 | 35 | 36 | 40 | 41 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /example/simple/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 29 | 30 | 31 | Fork me on Github 32 | 33 | 34 | 35 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /example/tap_state/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Tap State 5 | 6 | 39 | 40 | 41 | Fork me on Github 42 |
Tap Me
43 |
44 | 45 |
46 | 89 |
90 | 91 | 92 | -------------------------------------------------------------------------------- /example/test/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Event Test 5 | 6 | 14 | 15 | 16 |
Touch Me
17 |
18 | 19 |
20 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AlloyFinger 6 | 7 | 169 | 170 | 171 | Fork me on Github 172 |
AlloyFinger
173 |
174 |
pinch
175 |
176 | 177 |
178 |
rotate
179 |
180 | 181 |
182 |
pinch+rotate
183 |
184 | 185 |
186 |
pressMove
187 |
188 | 189 |
190 |
doubleTap
191 |
192 | 193 |
194 |
swipe
195 |
196 |
197 |
198 | 199 |
200 |
201 | 202 | 203 | 204 |
205 |
206 |
207 |
longTap
208 |
209 |
210 | 211 |
212 | 213 |
214 |
215 |
216 | 217 |
tap
218 |
219 |
220 | 221 |
222 | 223 |
224 |
225 |
226 |
227 | 230 | 231 | 232 | 233 | 374 | 375 | 376 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alloyfinger", 3 | "version": "0.1.15", 4 | "description": "super tiny size multi-touch gestures library for the web. ", 5 | "main": "alloy_finger.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "prepare": "npm run build", 11 | "build": "if [ -d dist ]; then rm -r dist; fi; npm run build:react", 12 | "build:react": "babel react --out-dir dist/react --source-map", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "devDependencies": { 16 | "babel-cli": "^6.24.0", 17 | "babel-core": "^6.24.0", 18 | "babel-plugin-add-module-exports": "^0.2.1", 19 | "babel-plugin-transform-class-properties": "^6.23.0", 20 | "babel-plugin-transform-decorators": "^6.24.1", 21 | "babel-plugin-transform-function-bind": "^6.22.0", 22 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 23 | "babel-polyfill": "^6.23.0", 24 | "babel-preset-env": "^1.3.2", 25 | "babel-preset-react": "^6.24.1", 26 | "babel-runtime": "^6.23.0", 27 | "fast-async": "^6.2.2", 28 | "husky": "^0.13.3" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/AlloyTeam/AlloyFinger.git" 33 | }, 34 | "keywords": [ 35 | "gesture", 36 | "touch", 37 | "multitouch" 38 | ], 39 | "author": "dntzhang", 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/AlloyTeam/AlloyFinger/issues" 43 | }, 44 | "homepage": "https://github.com/AlloyTeam/AlloyFinger#readme" 45 | } 46 | -------------------------------------------------------------------------------- /react/AlloyFinger.jsx: -------------------------------------------------------------------------------- 1 | /* AlloyFinger v0.1.0 2 | * By dntzhang 3 | * Reedited by nemoliao 4 | * Github: https://github.com/AlloyTeam/AlloyFinger 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | 9 | export default class AlloyFinger extends Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.preV = { x: null, y: null }; 14 | this.pinchStartLen = null; 15 | this.scale = 1; 16 | this.isSingleTap = false; 17 | this.isDoubleTap = false; 18 | this.delta = null; 19 | this.last = null; 20 | this.now = null; 21 | this.end = null; 22 | this.multiTouch = false; 23 | this.tapTimeout = null; 24 | this.longTapTimeout = null; 25 | this.singleTapTimeout = null; 26 | this.swipeTimeout=null; 27 | this.x1 = this.x2 = this.y1 = this.y2 = null; 28 | this.preTapPosition={x: null, y: null}; 29 | 30 | // Disable taps after longTap 31 | this.afterLongTap = false; 32 | this.afterLongTapTimeout = null; 33 | } 34 | 35 | getLen(v) { 36 | return Math.sqrt(v.x * v.x + v.y * v.y); 37 | } 38 | 39 | dot(v1, v2) { 40 | return v1.x * v2.x + v1.y * v2.y; 41 | } 42 | 43 | getAngle(v1, v2) { 44 | var mr = this.getLen(v1) * this.getLen(v2); 45 | if (mr === 0) return 0; 46 | var r = this.dot(v1, v2) / mr; 47 | if (r > 1) r = 1; 48 | return Math.acos(r); 49 | } 50 | 51 | cross(v1, v2) { 52 | return v1.x * v2.y - v2.x * v1.y; 53 | } 54 | 55 | getRotateAngle(v1, v2) { 56 | var angle = this.getAngle(v1, v2); 57 | if (this.cross(v1, v2) > 0) { 58 | angle *= -1; 59 | } 60 | 61 | return angle * 180 / Math.PI; 62 | } 63 | 64 | _resetState() { 65 | this.setState({ 66 | x: null, 67 | y: null, 68 | swiping: false, 69 | start: 0 70 | }); 71 | } 72 | 73 | 74 | _emitEvent(name, ...arg) { 75 | if (this.props[name]) { 76 | this.props[name](...arg); 77 | } 78 | } 79 | 80 | _handleTouchStart (evt) { 81 | this._emitEvent('onTouchStart', evt); 82 | if (!evt.touches) return; 83 | this.now = Date.now(); 84 | this.x1 = evt.touches[0].pageX; 85 | this.y1 = evt.touches[0].pageY; 86 | this.delta = this.now - (this.last || this.now); 87 | if (this.preTapPosition.x!==null) { 88 | this.isDoubleTap = (this.delta > 0 && this.delta <= 250&&Math.abs(this.preTapPosition.x-this.x1)<30&&Math.abs(this.preTapPosition.y-this.y1)<30); 89 | } 90 | this.preTapPosition.x=this.x1; 91 | this.preTapPosition.y=this.y1; 92 | this.last = this.now; 93 | var preV = this.preV, 94 | len = evt.touches.length; 95 | 96 | if (len > 1) { 97 | this._cancelLongTap(); 98 | this._cancelSingleTap(); 99 | var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 }; 100 | preV.x = v.x; 101 | preV.y = v.y; 102 | this.pinchStartLen = this.getLen(preV); 103 | this._emitEvent('onMultipointStart', evt); 104 | } else { 105 | this.isSingleTap = true; 106 | } 107 | this.longTapTimeout = setTimeout(() => { 108 | this._emitEvent('onLongTap', evt); 109 | this.afterLongTap = true; 110 | this.afterLongTapTimeout = setTimeout(() => { 111 | this.afterLongTap = false; 112 | }, 1000); 113 | }, 750); 114 | } 115 | 116 | _handleTouchMove(evt) { 117 | this._emitEvent('onTouchMove', evt); 118 | var preV = this.preV, 119 | len = evt.touches.length, 120 | currentX = evt.touches[0].pageX, 121 | currentY = evt.touches[0].pageY; 122 | this.isSingleTap = false; 123 | this.isDoubleTap = false; 124 | if (len > 1) { 125 | var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY }; 126 | if (preV.x !== null) { 127 | if (this.pinchStartLen > 0) { 128 | evt.center = { 129 | x: (evt.touches[1].pageX + currentX) / 2, 130 | y: (evt.touches[1].pageY + currentY) / 2 131 | }; 132 | evt.scale = evt.zoom = this.getLen(v) / this.pinchStartLen; 133 | this._emitEvent('onPinch', evt); 134 | } 135 | evt.angle = this.getRotateAngle(v, preV); 136 | this._emitEvent('onRotate', evt); 137 | } 138 | preV.x = v.x; 139 | preV.y = v.y; 140 | this.multiTouch = true; 141 | } else { 142 | if (this.x2 !== null) { 143 | evt.deltaX = currentX - this.x2; 144 | evt.deltaY = currentY - this.y2; 145 | } else { 146 | evt.deltaX = 0; 147 | evt.deltaY = 0; 148 | } 149 | this._emitEvent('onPressMove', evt); 150 | } 151 | this._cancelLongTap(); 152 | this.x2 = currentX; 153 | this.y2 = currentY; 154 | 155 | if (len > 1) { 156 | evt.preventDefault(); 157 | } 158 | } 159 | 160 | _handleTouchCancel(evt) { 161 | this._emitEvent('onTouchCancel', evt); 162 | clearInterval(this.singleTapTimeout); 163 | clearInterval(this.tapTimeout); 164 | clearInterval(this.longTapTimeout); 165 | clearInterval(this.swipeTimeout); 166 | } 167 | 168 | _handleTouchEnd(evt) { 169 | this._emitEvent('onTouchEnd', evt); 170 | this.end = Date.now(); 171 | this._cancelLongTap(); 172 | 173 | if (this.multiTouch === true && evt.touches.length < 2) { 174 | this._emitEvent('onMultipointEnd', evt); 175 | } 176 | 177 | evt.origin = [this.x1, this.y1]; 178 | if (this.multiTouch === false) { 179 | if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) || 180 | (this.y2 && Math.abs(this.y1 - this.y2) > 30)) { 181 | evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2); 182 | evt.distance = Math.abs(this.x1 - this.x2); 183 | this.swipeTimeout = setTimeout(() => { 184 | this._emitEvent('onSwipe', evt); 185 | }, 0); 186 | } else { 187 | if (this.afterLongTap) { 188 | clearTimeout(this.afterLongTapTimeout); 189 | this.afterLongTap = false; 190 | } else { 191 | this.tapTimeout = setTimeout(() => { 192 | this._emitEvent('onTap', evt); 193 | if (this.isDoubleTap) { 194 | this._emitEvent('onDoubleTap', evt); 195 | clearTimeout(this.singleTapTimeout); 196 | this.isDoubleTap = false; 197 | } else if (this.isSingleTap) { 198 | this.singleTapTimeout = setTimeout(()=>{ 199 | this._emitEvent('onSingleTap', evt); 200 | }, 250); 201 | this.isSingleTap = false; 202 | } 203 | }, 0); 204 | } 205 | } 206 | } 207 | 208 | this.preV.x = 0; 209 | this.preV.y = 0; 210 | this.scale = 1; 211 | this.pinchStartLen = null; 212 | this.x1 = this.x2 = this.y1 = this.y2 = null; 213 | this.multiTouch = false; 214 | } 215 | 216 | _cancelLongTap () { 217 | clearTimeout(this.longTapTimeout); 218 | } 219 | 220 | _cancelSingleTap () { 221 | clearTimeout(this.singleTapTimeout); 222 | } 223 | 224 | _swipeDirection (x1, x2, y1, y2) { 225 | if (Math.abs(x1 - x2) > 80 || this.end-this.now < 250) { 226 | return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down'); 227 | } else { 228 | return 'Nochange'; 229 | } 230 | 231 | } 232 | 233 | render() { 234 | return React.cloneElement(React.Children.only(this.props.children), { 235 | onTouchStart: this._handleTouchStart.bind(this), 236 | onTouchMove: this._handleTouchMove.bind(this), 237 | onTouchCancel: this._handleTouchCancel.bind(this), 238 | onTouchEnd: this._handleTouchEnd.bind(this) 239 | }); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /transformjs/README.md: -------------------------------------------------------------------------------- 1 | 2 | transformjs have been moved to here https://github.com/AlloyTeam/AlloyTouch/tree/master/transformjs 3 | 4 | -------------------------------------------------------------------------------- /transformjs/transform.js: -------------------------------------------------------------------------------- 1 | /* transformjs 2 | * By dntzhang 3 | * Github: https://github.com/AlloyTeam/AlloyTouch/tree/master/transformjs 4 | */ 5 | ;(function () { 6 | 7 | var Matrix3D = function (n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { 8 | this.elements =window.Float32Array ? new Float32Array(16) : []; 9 | var te = this.elements; 10 | te[0] = (n11 !== undefined) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0; 11 | te[1] = n21 || 0; te[5] = (n22 !== undefined) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0; 12 | te[2] = n31 || 0; te[6] = n32 || 0; te[10] = (n33 !== undefined) ? n33 : 1; te[14] = n34 || 0; 13 | te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = (n44 !== undefined) ? n44 : 1; 14 | }; 15 | 16 | Matrix3D.DEG_TO_RAD = Math.PI / 180; 17 | 18 | Matrix3D.prototype = { 19 | set: function (n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { 20 | var te = this.elements; 21 | te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; 22 | te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; 23 | te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; 24 | te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; 25 | return this; 26 | }, 27 | identity: function () { 28 | this.set( 29 | 1, 0, 0, 0, 30 | 0, 1, 0, 0, 31 | 0, 0, 1, 0, 32 | 0, 0, 0, 1 33 | ); 34 | return this; 35 | }, 36 | multiplyMatrices: function (a, be) { 37 | 38 | var ae = a.elements; 39 | var te = this.elements; 40 | var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; 41 | var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; 42 | var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; 43 | var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; 44 | 45 | var b11 = be[0], b12 = be[1], b13 = be[2], b14 = be[3]; 46 | var b21 = be[4], b22 = be[5], b23 = be[6], b24 = be[7]; 47 | var b31 = be[8], b32 = be[9], b33 = be[10], b34 = be[11]; 48 | var b41 = be[12], b42 = be[13], b43 = be[14], b44 = be[15]; 49 | 50 | te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 51 | te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 52 | te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 53 | te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 54 | 55 | te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 56 | te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 57 | te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 58 | te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 59 | 60 | te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 61 | te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 62 | te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 63 | te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 64 | 65 | te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 66 | te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 67 | te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 68 | te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 69 | 70 | return this; 71 | 72 | }, 73 | // 解决角度为90的整数倍导致Math.cos得到极小的数,其实是0。导致不渲染 74 | _rounded: function(value,i){ 75 | i= Math.pow(10, i || 15); 76 | // default 77 | return Math.round(value*i)/i; 78 | }, 79 | appendTransform: function (x, y, z, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ,skewX,skewY, originX, originY, originZ) { 80 | 81 | var rx = rotateX * Matrix3D.DEG_TO_RAD; 82 | var cosx =this._rounded( Math.cos(rx)); 83 | var sinx = this._rounded(Math.sin(rx)); 84 | var ry = rotateY * Matrix3D.DEG_TO_RAD; 85 | var cosy =this._rounded( Math.cos(ry)); 86 | var siny = this._rounded(Math.sin(ry)); 87 | var rz = rotateZ * Matrix3D.DEG_TO_RAD; 88 | var cosz =this._rounded( Math.cos(rz * -1)); 89 | var sinz =this._rounded( Math.sin(rz * -1)); 90 | 91 | this.multiplyMatrices(this, [ 92 | 1, 0, 0, x, 93 | 0, cosx, sinx, y, 94 | 0, -sinx, cosx, z, 95 | 0, 0, 0, 1 96 | ]); 97 | 98 | this.multiplyMatrices(this, [ 99 | cosy, 0, siny, 0, 100 | 0, 1, 0, 0, 101 | -siny, 0, cosy, 0, 102 | 0, 0, 0, 1 103 | ]); 104 | 105 | this.multiplyMatrices(this,[ 106 | cosz * scaleX, sinz * scaleY, 0, 0, 107 | -sinz * scaleX, cosz * scaleY, 0, 0, 108 | 0, 0, 1 * scaleZ, 0, 109 | 0, 0, 0, 1 110 | ]); 111 | 112 | if(skewX||skewY){ 113 | this.multiplyMatrices(this,[ 114 | this._rounded(Math.cos(skewX* Matrix3D.DEG_TO_RAD)), this._rounded( Math.sin(skewX* Matrix3D.DEG_TO_RAD)), 0, 0, 115 | -1*this._rounded(Math.sin(skewY* Matrix3D.DEG_TO_RAD)), this._rounded( Math.cos(skewY* Matrix3D.DEG_TO_RAD)), 0, 0, 116 | 0, 0, 1, 0, 117 | 0, 0, 0, 1 118 | ]); 119 | } 120 | 121 | if (originX || originY || originZ) { 122 | this.elements[12] -= originX * this.elements[0] + originY * this.elements[4] + originZ * this.elements[8]; 123 | this.elements[13] -= originX * this.elements[1] + originY * this.elements[5] + originZ * this.elements[9]; 124 | this.elements[14] -= originX * this.elements[2] + originY * this.elements[6] + originZ * this.elements[10]; 125 | } 126 | return this; 127 | } 128 | }; 129 | 130 | function observe(target, props, callback) { 131 | for (var i = 0, len = props.length; i < len; i++) { 132 | var prop = props[i]; 133 | watch(target, prop, callback); 134 | } 135 | } 136 | 137 | function watch(target, prop, callback) { 138 | Object.defineProperty(target, prop, { 139 | get: function () { 140 | return this["__" + prop]; 141 | }, 142 | set: function (value) { 143 | if (value !== this["__" + prop]) { 144 | this["__" + prop] = value; 145 | callback(); 146 | } 147 | 148 | } 149 | }); 150 | } 151 | 152 | window.Transform = function (element,notPerspective) { 153 | 154 | observe( 155 | element, 156 | ["translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ", "rotateX", "rotateY", "rotateZ", "skewX", "skewY", "originX", "originY", "originZ"], 157 | function () { 158 | var mtx = element.matrix3D.identity().appendTransform(element.translateX, element.translateY, element.translateZ, element.scaleX, element.scaleY, element.scaleZ, element.rotateX, element.rotateY, element.rotateZ, element.skewX, element.skewY, element.originX, element.originY, element.originZ); 159 | element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform =(notPerspective?"":"perspective(" + (element.perspective===undefined?500:element.perspective) + "px) ")+ "matrix3d(" + Array.prototype.slice.call(mtx.elements).join(",") + ")"; 160 | }); 161 | 162 | element.matrix3D = new Matrix3D(); 163 | 164 | if (!notPerspective) { 165 | observe( 166 | element, 167 | ["perspective"], 168 | function () { 169 | element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = "perspective(" + element.perspective + "px) matrix3d(" + Array.prototype.slice.call(element.matrix3D.elements).join(",") + ")"; 170 | }); 171 | element.perspective = 500; 172 | } 173 | 174 | element.scaleX = element.scaleY = element.scaleZ = 1; 175 | //由于image自带了x\y\z,所有加上translate前缀 176 | element.translateX = element.translateY = element.translateZ = element.rotateX = element.rotateY = element.rotateZ = element.skewX = element.skewY = element.originX = element.originY = element.originZ = 0; 177 | } 178 | })(); 179 | --------------------------------------------------------------------------------