/** * @filename Ex.plugin.Pinchemu.js * * @name Pinch emulator plugin for Sencha Touch * @fileOverview Emulation of double touch pinch event for desktops * * @author Constantine V. Smirnov kostysh(at)gmail.com * @date 20120220 * @version 0.2 * @license Free * * @requires Sencha Touch 2.0 * * Usage: ..... items: [ { xtype: 'panel', id: 'mypinchitem', plugins: [ { xclass: 'Ext.plugin.Pinchemu', helpers: true//enable touches visualization } ] } ] * */ Ext.define('DE.plugin.Pinchemu', { extend: 'Ext.Component', alias: 'plugin.pinchemu', config: { helpers: true }, init: function(cmp) { var self = this; self.touchHelpers = []; self.touchHelpers[0] = Ext.create('Ext.Button', { top: 0, left: 0, style: 'opacity: 0.6;', iconMask: true, round: true, hidden: true }); self.touchHelpers[1] = Ext.create('Ext.Button', { top: 0, left: 0, style: 'opacity: 0.6;', iconMask: true, round: true, hidden: true }); Ext.Viewport.add(self.touchHelpers[0]); Ext.Viewport.add(self.touchHelpers[1]); self.cmp = cmp; self.cmp.on({ scope: self, painted: self.initPinchsim }); }, //Plugin initialisation initPinchsim: function() { var self = this; this.pinchStarted = false; var item = self.cmp; if (!item.pinchSimEnabled) { if (item.rendered) { self.initHandlers(item); } else { item.on({ painted: self.initHandlers }); } } }, initHandlers: function(item) { var self = this; //Setup touch handlers on enabled item item.element.on({ scope: self, touchstart: function(ev) { if ((ev.event.ctrlKey || ev.event.shiftKey) && self.pinchStarted === false) { self.pinchStarted = true; if (ev.event.ctrlKey) { self.zoomStart = 100; self.zoomDirection = 1; } else if (ev.event.shiftKey) { self.zoomStart = 340; self.zoomDirection = -1; } self.zoomFactor = 1; self.onTouchStart(item, ev); } }, touchend: function(ev) { if (self.pinchStarted) { self.pinchStarted = false; self.onTouchEnd(item, ev); } }, touchcancel: function(ev) { if (self.pinchStarted) { self.pinchStarted = false; self.onTouchEnd(item, ev); } }, touchmove: function(ev) { if ((ev.event.ctrlKey || ev.event.shiftKey) && this.pinchStarted === true) { self.onTouchMove(item, ev); } else if (self.pinchStarted) { self.pinchStarted = false; self.onTouchEnd(item, ev); } } }); item.pinchSimEnabled = true; }, showHelpers: function(ev) { var touches = ev.touches; if (typeof touches === 'object' && this.getHelpers()) { this.moveHelpers(touches); this.setHelpersArrows(ev); this.touchHelpers[0].show(); this.touchHelpers[1].show(); } }, setHelpersArrows: function(ev) { if (ev.event.ctrlKey) { this.touchHelpers[0].setIconCls('arrow_right'); this.touchHelpers[1].setIconCls('arrow_left'); } else { this.touchHelpers[0].setIconCls('arrow_left'); this.touchHelpers[1].setIconCls('arrow_right'); } }, moveHelpers: function(touches) { this.touchHelpers[0].setTop(touches[0].point.y); this.touchHelpers[0].setLeft(touches[0].point.x); this.touchHelpers[1].setTop(touches[1].point.y); this.touchHelpers[1].setLeft(touches[1].point.x); }, hideHelpers: function() { this.touchHelpers[0].hide(); this.touchHelpers[1].hide(); }, //Converting of single touch event to double touch convertEvent: function(ev) { var self = this; //Clone of original touch object var touches = Array.prototype.slice.call(ev.touches); if (!touches) { touches = self.lastTouches;//at the pinchend only } ev.touches = touches; if (touches.length > 0) { if (!self.touchStartPoint) { var startX = touches[0].point.x; var startY = touches[0].point.y; var startPageX = touches[0].pageX; var startPageY = touches[0].pageY; touches[0].point.x = touches[0].point.x + self.zoomStart / 2; touches[0].pageX = touches[0].pageX + self.zoomStart / 2; //Build new touch point touches[1] = {}; touches[1].identifier = 2; touches[1].pageX = startPageX - self.zoomStart / 2; touches[1].pageY = startPageY; touches[1].point = touches[0].point.clone(); touches[1].point.x = startX - self.zoomStart / 2; touches[1].point.y = touches[0].point.y; touches[1].target = touches[0].target; touches[1].targets = touches[0].targets; touches[1].timeStamp = touches[0].timeStamp; //Remember the current start point this.touchStartPoint = { x: startX, y: startY, pageX: startPageX, pageY: startPageY, distance: touches[0].point.getDistanceTo(touches[1].point) }; } else { touches[0].point = self.lastTouches[0].point.clone();//replace original by previous touches[0].point.x = Ext.Number.constrain(self.lastTouches[0].point.x + self.zoomFactor * self.zoomDirection, self.touchStartPoint.x + self.zoomFactor); touches[0].pageX = Ext.Number.constrain(self.lastTouches[0].pageX + self.zoomFactor * self.zoomDirection, self.touchStartPoint.x + self.zoomFactor); touches[1] = {}; touches[1].point = self.lastTouches[1].point.clone(); touches[1].point.x = Ext.Number.constrain(self.lastTouches[1].point.x - self.zoomFactor * self.zoomDirection, self.touchStartPoint.x + self.zoomFactor); touches[1].pageX = Ext.Number.constrain(self.lastTouches[1].pageX - self.zoomFactor * self.zoomDirection, self.touchStartPoint.x + self.zoomFactor); touches[1].pageY = self.lastTouches[1].pageY; touches[1].target = touches[0].target; touches[1].targets = touches[0].targets; touches[1].timeStamp = touches[0].timeStamp; } self.lastTouches = touches; } ev.scale = self.getNewScale(ev); return ev; }, getNewScale: function(ev) { var self = this; if (ev.touches.length > 0) { var newDistance = ev.touches[0].point.getDistanceTo(ev.touches[1].point); self.lastScale = newDistance / self.touchStartPoint.distance; return self.lastScale; } else { return self.lastScale; } }, onTouchStart: function() { this.lastScale = 1; var ev = this.convertEvent(arguments[1]); this.showHelpers(ev); }, onTouchMove: function() { var ev = this.convertEvent(arguments[1]); this.lastTouches = Array.prototype.slice.call(ev.touches); this.moveHelpers(ev.touches); }, onTouchEnd: function() { var ev = this.convertEvent(arguments[1]); this.hideHelpers(); this.touchStartPoint = null; this.lastTouches = null; this.lastScale = null; } });