mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-03-13 16:33:28 -04:00
Implement propper support for pointer events and MSPointer events.
Closes #406 and #336.
This commit is contained in:
parent
5f00c02e67
commit
9c552b1739
4 changed files with 132 additions and 74 deletions
|
@ -46,6 +46,24 @@ var DomElement = new function() {
|
|||
return res;
|
||||
}
|
||||
|
||||
// Handles both getting and setting of vendor prefix values
|
||||
function handlePrefix(el, name, set, value) {
|
||||
var prefixes = ['webkit', 'moz', 'Moz', 'ms', 'o', ''],
|
||||
suffix = name[0].toUpperCase() + name.substring(1);
|
||||
for (var i = 0; i < 6; i++) {
|
||||
var prefix = prefixes[i],
|
||||
key = prefix ? prefix + suffix : name;
|
||||
if (key in el) {
|
||||
if (set) {
|
||||
el[key] = value;
|
||||
} else {
|
||||
return el[key];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return /** @lends DomElement */{
|
||||
create: function(nodes, parent) {
|
||||
var isArray = Array.isArray(nodes),
|
||||
|
@ -203,13 +221,17 @@ var DomElement = new function() {
|
|||
* Gets the given property from the element, trying out all browser
|
||||
* prefix variants.
|
||||
*/
|
||||
getPrefixValue: function(el, name) {
|
||||
var value = el[name],
|
||||
prefixes = ['webkit', 'moz', 'ms', 'o'],
|
||||
suffix = name[0].toUpperCase() + name.substring(1);
|
||||
for (var i = 0; i < 4 && value == null; i++)
|
||||
value = el[prefixes[i] + suffix];
|
||||
return value;
|
||||
getPrefixed: function(el, name) {
|
||||
return handlePrefix(el, name);
|
||||
},
|
||||
|
||||
setPrefixed: function(el, name, value) {
|
||||
if (typeof name === 'object') {
|
||||
for (var key in name)
|
||||
handlePrefix(el, key, true, name[key]);
|
||||
} else {
|
||||
handlePrefix(el, name, true, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,13 +17,21 @@
|
|||
*/
|
||||
var DomEvent = /** @lends DomEvent */{
|
||||
add: function(el, events) {
|
||||
for (var type in events)
|
||||
el.addEventListener(type, events[type], false);
|
||||
for (var type in events) {
|
||||
var func = events[type],
|
||||
parts = type.split(/[\s,]+/g);
|
||||
for (var i = 0, l = parts.length; i < l; i++)
|
||||
el.addEventListener(parts[i], func, false);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function(el, events) {
|
||||
for (var type in events)
|
||||
el.removeEventListener(type, events[type], false);
|
||||
for (var type in events) {
|
||||
var func = events[type],
|
||||
parts = type.split(/[\s,]+/g);
|
||||
for (var i = 0, l = parts.length; i < l; i++)
|
||||
el.removeEventListener(parts[i], func, false);
|
||||
}
|
||||
},
|
||||
|
||||
getPoint: function(event) {
|
||||
|
@ -59,8 +67,7 @@ var DomEvent = /** @lends DomEvent */{
|
|||
};
|
||||
|
||||
DomEvent.requestAnimationFrame = new function() {
|
||||
var nativeRequest = DomElement.getPrefixValue(window,
|
||||
'requestAnimationFrame'),
|
||||
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
|
||||
requested = false,
|
||||
callbacks = [],
|
||||
focused = true,
|
||||
|
|
|
@ -51,7 +51,7 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
|||
// Hi-DPI Canvas support based on:
|
||||
// http://www.html5rocks.com/en/tutorials/canvas/hidpi/
|
||||
var deviceRatio = window.devicePixelRatio || 1,
|
||||
backingStoreRatio = DomElement.getPrefixValue(this._context,
|
||||
backingStoreRatio = DomElement.getPrefixed(this._context,
|
||||
'backingStorePixelRatio') || 1;
|
||||
this._pixelRatio = deviceRatio / backingStoreRatio;
|
||||
}
|
||||
|
|
149
src/ui/View.js
149
src/ui/View.js
|
@ -38,7 +38,20 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
if (this._id == null)
|
||||
element.setAttribute('id', this._id = 'view-' + View._id++);
|
||||
// Install event handlers
|
||||
DomEvent.add(element, this._viewHandlers);
|
||||
DomEvent.add(element, this._viewEvents);
|
||||
// Borrowed from Hammer.js:
|
||||
var none = 'none';
|
||||
DomElement.setPrefixed(element.style, {
|
||||
userSelect: none,
|
||||
// This makes the element blocking in IE10+
|
||||
// You could experiment with the value, see this issue:
|
||||
// https://github.com/EightMedia/hammer.js/issues/241
|
||||
touchAction: none,
|
||||
touchCallout: none,
|
||||
contentZooming: none,
|
||||
userDrag: none,
|
||||
tapHighlightColor: 'rgba(0,0,0,0)'
|
||||
});
|
||||
// If the element has the resize attribute, resize the it to fill the
|
||||
// window and resize it again whenever the user resizes the window.
|
||||
if (PaperScope.hasAttribute(element, 'resize')) {
|
||||
|
@ -48,7 +61,7 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
that = this;
|
||||
size = DomElement.getViewportBounds(element)
|
||||
.getSize().subtract(offset);
|
||||
this._windowHandlers = {
|
||||
this._windowEvents = {
|
||||
resize: function() {
|
||||
// Only update element offset if it's not invisible, as
|
||||
// otherwise the offset would be wrong.
|
||||
|
@ -60,7 +73,7 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
.getSize().subtract(offset));
|
||||
}
|
||||
};
|
||||
DomEvent.add(window, this._windowHandlers);
|
||||
DomEvent.add(window, this._windowEvents);
|
||||
} else {
|
||||
// Try visible size first, since that will help handling previously
|
||||
// scaled canvases (e.g. when dealing with pixel-ratio)
|
||||
|
@ -130,8 +143,8 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
this._project.view = null;
|
||||
/*#*/ if (__options.environment == 'browser') {
|
||||
// Uninstall event handlers again for this view.
|
||||
DomEvent.remove(this._element, this._viewHandlers);
|
||||
DomEvent.remove(window, this._windowHandlers);
|
||||
DomEvent.remove(this._element, this._viewEvents);
|
||||
DomEvent.remove(window, this._windowEvents);
|
||||
/*#*/ } // __options.environment == 'browser'
|
||||
this._element = this._project = null;
|
||||
// Remove all onFrame handlers.
|
||||
|
@ -687,7 +700,68 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
}
|
||||
}
|
||||
|
||||
function mousedown(event) {
|
||||
function handleMouseMove(view, point, event) {
|
||||
view._handleEvent('mousemove', point, event);
|
||||
var tool = view._scope.tool;
|
||||
if (tool) {
|
||||
// If there's no onMouseDrag, fire onMouseMove while dragging.
|
||||
tool._handleEvent(dragging && tool.responds('mousedrag')
|
||||
? 'mousedrag' : 'mousemove', point, event);
|
||||
}
|
||||
view.update();
|
||||
return tool;
|
||||
}
|
||||
|
||||
// Touch handling inspired by Hammer.js
|
||||
var navigator = window.navigator,
|
||||
mousedown, mousemove, mouseup;
|
||||
if (navigator.pointerEnabled || navigator.msPointerEnabled) {
|
||||
// HTML5 / MS pointer events
|
||||
mousedown = 'pointerdown MSPointerDown';
|
||||
mousemove = 'pointermove MSPointerMove';
|
||||
mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
|
||||
} else {
|
||||
mousedown = 'touchstart';
|
||||
mousemove = 'touchmove';
|
||||
mouseup = 'touchend touchcancel';
|
||||
// Do not add mouse events on mobile and tablet devices
|
||||
if (!('ontouchstart' in window && navigator.userAgent.match(
|
||||
/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
|
||||
// For non pointer events browsers and mixed browsers, like chrome
|
||||
// on Windows8 touch laptop.
|
||||
mousedown += ' mousedown';
|
||||
mousemove += ' mousemove';
|
||||
mouseup += ' mouseup';
|
||||
}
|
||||
}
|
||||
|
||||
var viewEvents = {
|
||||
selectstart: function(event) {
|
||||
// Only stop this even if we're dragging already, since otherwise no
|
||||
// text whatsoever can be selected on the page.
|
||||
if (dragging)
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
var docEvents = {
|
||||
mouseout: function(event) {
|
||||
// When the moues leaves the document, fire one last mousemove
|
||||
// event, to give items the change to receive a mouseleave, etc.
|
||||
var view = View._focused,
|
||||
target = DomEvent.getRelatedTarget(event);
|
||||
if (view && (!target || target.nodeName === 'HTML'))
|
||||
handleMouseMove(view, viewToProject(view, event), event);
|
||||
},
|
||||
|
||||
scroll: updateFocus
|
||||
};
|
||||
|
||||
// mousemove and mouseup events need to be installed on document, not the
|
||||
// view element, since we want to catch the end of drag events even outside
|
||||
// our view. Only the mousedown events are installed on the view, as defined
|
||||
// by _viewEvents below.
|
||||
viewEvents[mousedown] = function(event) {
|
||||
// Get the view from the event, and store a reference to the view that
|
||||
// should receive keyboard input.
|
||||
var view = View._focused = getView(event),
|
||||
|
@ -701,29 +775,17 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
// In the end we always call update(), which only updates the view if
|
||||
// anything has changed in the above calls.
|
||||
view.update();
|
||||
}
|
||||
};
|
||||
|
||||
function handleMouseMove(view, point, event) {
|
||||
view._handleEvent('mousemove', point, event);
|
||||
var tool = view._scope.tool;
|
||||
if (tool) {
|
||||
// If there's no onMouseDrag, fire onMouseMove while dragging.
|
||||
tool._handleEvent(dragging && tool.responds('mousedrag')
|
||||
? 'mousedrag' : 'mousemove', point, event);
|
||||
}
|
||||
view.update();
|
||||
return tool;
|
||||
}
|
||||
|
||||
function mousemove(event) {
|
||||
docEvents[mousemove] = function(event) {
|
||||
var view = View._focused;
|
||||
if (!dragging) {
|
||||
// See if we can get the view from the current event target, and
|
||||
// handle the mouse move over it.
|
||||
var target = getView(event);
|
||||
if (target) {
|
||||
// Temporarily focus this view without making it sticky, so
|
||||
// Key events are handled too during the mouse over
|
||||
// Temporarily focus this view without making it sticky, so Key
|
||||
// events are handled too during the mouse over.
|
||||
// If we switch view, fire one last mousemove in the old view,
|
||||
// to give items the change to receive a mouseleave, etc.
|
||||
if (view !== target)
|
||||
|
@ -741,18 +803,9 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
if (dragging || view.getBounds().contains(point))
|
||||
tool = handleMouseMove(view, point, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function mouseout(event) {
|
||||
// When the moues leaves the document, fire one last mousemove event,
|
||||
// to give items the change to receive a mouseleave, etc.
|
||||
var view = View._focused,
|
||||
target = DomEvent.getRelatedTarget(event);
|
||||
if (view && (!target || target.nodeName === 'HTML'))
|
||||
handleMouseMove(view, viewToProject(view, event), event);
|
||||
}
|
||||
|
||||
function mouseup(event) {
|
||||
docEvents[mouseup] = function(event) {
|
||||
var view = View._focused;
|
||||
if (!view || !dragging)
|
||||
return;
|
||||
|
@ -763,40 +816,16 @@ var View = Base.extend(Callback, /** @lends View# */{
|
|||
if (tool)
|
||||
tool._handleEvent('mouseup', point, event);
|
||||
view.update();
|
||||
}
|
||||
};
|
||||
|
||||
function selectstart(event) {
|
||||
// Only stop this even if we're dragging already, since otherwise no
|
||||
// text whatsoever can be selected on the page.
|
||||
if (dragging)
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// mousemove and mouseup events need to be installed on document, not the
|
||||
// view element, since we want to catch the end of drag events even outside
|
||||
// our view. Only the mousedown events are installed on the view, as handled
|
||||
// by _createHandlers below.
|
||||
|
||||
DomEvent.add(document, {
|
||||
mousemove: mousemove,
|
||||
mouseout: mouseout,
|
||||
mouseup: mouseup,
|
||||
touchmove: mousemove,
|
||||
touchend: mouseup,
|
||||
selectstart: selectstart,
|
||||
scroll: updateFocus
|
||||
});
|
||||
DomEvent.add(document, docEvents);
|
||||
|
||||
DomEvent.add(window, {
|
||||
load: updateFocus
|
||||
});
|
||||
|
||||
return {
|
||||
_viewHandlers: {
|
||||
mousedown: mousedown,
|
||||
touchstart: mousedown,
|
||||
selectstart: selectstart
|
||||
},
|
||||
_viewEvents: viewEvents,
|
||||
|
||||
// To be defined in subclasses
|
||||
_handleEvent: function(/* type, point, event */) {},
|
||||
|
|
Loading…
Reference in a new issue