Related
I am making a small custom drag and drop library, but am running into an issue where mousemove/mouseup events don't fire outside of the window with user-select: none specified. I found a relevant stackoverflow question, but it refrences old IE/FF versions as the issue, wheras my drag and drop implementation doesn't work in any newer browsers.
Am I doing something wrong?
Relevant code (obviously code is not finished, just trying to get a kinda working implementation atm):
Draggable.vue Mount
this.$el.addEventListener('mousedown', (e) => {
if (e.buttons === 1) {
this.mouseStart = {
x: e.clientX,
y: e.clientY,
}
this.$el.addEventListener('mousemove', onMouseMove)
this.$el.addEventListener('mouseup', onMouseUp)
}
})
const onMouseMove = (e) => {
if (this.mouseStart) {
if (!dnd.dragging && Math.abs(this.mouseStart.x - e.clientX) > 2 ||
Math.abs(this.mouseStart.y - e.clientY) > 2
) {
dnd.startDrag(
{
x: this.mouseStart.x,
y: this.mouseStart.y,
},
this.$el
)
this.mouseStart = null
}
}
}
const onMouseUp = (e) => {
this.$el.removeEventListener('mousemove', onMouseMove)
this.$el.removeEventListener('mouseup', onMouseUp)
this.mouseStart = null
}
Dnd.js
class Dnd extends EventEmitter {
dragging = false
mouseStartPos = null
dragEle = null
dragEleStartPos = null
ghostComp = null
onMouseUp = () => {
this.stopDrag()
}
onMouseMove = (e) => {
this.dragEle.style.top =
this.dragEleStartPos.y + e.clientY - this.mouseStartPos.y + 'px'
this.dragEle.style.left =
this.dragEleStartPos.x + e.clientX - this.mouseStartPos.x + 'px'
}
startDrag(ctx, comp) {
this.ghostComp = comp
this.mouseStartPos = {
x: ctx.x,
y: ctx.y,
}
this.dragging = true
this.dragEle = this.createDragElement(comp)
document.body.addEventListener('mouseup', this.onMouseUp)
document.body.addEventListener('mousemove', this.onMouseMove)
this.ghostComp.classList.add('dnd-ghost')
}
endDrag() {
this.dragEle.remove()
this.ghostComp.classList.remove('dnd-ghost')
this.dragging = false
this.mouseStartPos = null
this.dragEle = null
this.ghostComp = null
}
stopDrag() {
document.body.removeEventListener('mouseup', this.onMouseUp)
document.body.removeEventListener('mousemove', this.onMouseMove)
this.dragEle.classList.add('dnd-transition')
this.dragEle.style.top = this.dragEleStartPos.y + 'px'
this.dragEle.style.left = this.dragEleStartPos.x + 'px'
setTimeout(this.endDrag.bind(this), 500)
}
createDragElement(node) {
const cln = node.cloneNode(true)
cln.classList.add('dnd-dragging')
const rect = node.getBoundingClientRect()
document.body.appendChild(cln)
cln.style.top = rect.top + 'px'
cln.style.left = rect.left + 'px'
cln.style.width = rect.width + 'px'
cln.style.height = rect.height + 'px'
this.dragEleStartPos = { x: rect.left, y: rect.top }
return cln
}
}
export default new Dnd()
I was able to resolve this by attaching the event listener to window, and not document.body
So i am trying to implement simple touch controls on a javascript game. I have the following answer from a search:
Snake Game with Controller Buttons for Mobile Use **UPDATED**
However I was trying to change this jquery into javascript so that it would work with my game
Jquery:
$(document).on('click', '.button-pad > button', function(e) {
if ($(this).hasClass('left-btn')) {
e = 37;
}
Javascript:
var contoller = document.getElementById("button-pad").on('click',
'.button-pad > button', function(e) {
if ('.button-pad > button'(this).hasClass('btn-left')) {
e = 37;
}
I thought I had it sorted but it is not working at all
Codepen here:
https://codepen.io/MrVincentRyan/pen/VqpMrJ?editors=1010
Your existing code has some problems with it, but it was close enough where I could translate it. However, your current code seems to want to reassign the event argument being passed to the click handler (e) to 37. This makes no sense. Most likely you just want another variable set to 37 and that's what I've done below:
spaceInvader(window, document.getElementById('space-invader'));
window.focus();
let game = null;
let ship = null;
function spaceInvader (window, canvas) {
canvas.focus();
var context = canvas.getContext('2d');
/* GAME */
function Game () {
this.message = '';
this.rebel = [];
this.republic = [];
this.other = [];
this.size = {x: canvas.width, y: canvas.height};
this.wave = 0;
this.refresh = function () {
this.update();
this.draw();
requestAnimationFrame(this.refresh);
}.bind(this);
this.init();
}
Game.MESSAGE_DURATION = 1500;
Game.prototype.init = function () {
this.ship = new Ship(this);
this.addRebel(this.ship);
this.refresh();
};
Game.prototype.update = function () {
this.handleCollisions();
this.computeElements();
this.elements.forEach(Element.update);
if (!this.rebel.length) {
this.showText('Gatwick closed', true);
return;
}
if (!this.republic.length) this.createWave();
};
Game.prototype.draw = function () {
context.clearRect(0, 0, this.size.x, this.size.y);
this.elements.forEach(Element.draw);
Alien.drawLife(this.republic);
if (this.message) {
context.save();
context.font = '30px Arial';
context.textAlign='center';
context.fillStyle = '#FFFFFF';
context.fillText(this.message, canvas.width / 2, canvas.height / 2);
context.restore();
}
};
Game.prototype.computeElements = function () {
this.elements = this.other.concat(this.republic, this.rebel);
};
Game.prototype.addRebel = function (element) {
this.rebel.push(element);
};
Game.prototype.addRepublic = function (element) {
this.republic.push(element);
};
Game.prototype.addOther = function (element) {
this.other.push(element);
};
Game.prototype.handleCollisions = function () {
this.rebel.forEach(function(elementA) {
this.republic.forEach(function (elementB) {
if (!Element.colliding(elementA, elementB)) return;
elementA.life--;
elementB.life--;
var sizeA = elementA.size.x * elementA.size.y;
var sizeB = elementB.size.x * elementB.size.y;
this.addOther(new Explosion(this, sizeA > sizeB ? elementA.pos : elementB.pos));
}, this);
}, this);
this.republic = this.republic.filter(Element.isAlive);
this.rebel = this.rebel.filter(Element.isAlive);
this.other = this.other.filter(Element.isAlive);
this.republic = this.republic.filter(this.elementInGame, this);
this.rebel = this.rebel.filter(this.elementInGame, this);
};
Game.prototype.elementInGame = function (element) {
return !(element instanceof Bullet) || (
element.pos.x + element.halfWidth > 0 &&
element.pos.x - element.halfWidth < this.size.x &&
element.pos.y + element.halfHeight > 0 &&
element.pos.y - element.halfHeight < this.size.x
);
};
Game.prototype.createWave = function () {
this.ship.life = Ship.MAX_LIFE;
this.ship.fireRate = Math.max(50, Ship.FIRE_RATE - 50 * this.wave);
this.wave++;
this.showText('Wave: ' + this.wave);
var waveSpeed = Math.ceil(this.wave / 2);
var waveProb = (999 - this.wave * 2) / 1000;
var margin = {x: Alien.SIZE.x + 10, y: Alien.SIZE.y + 10};
for (var i = 0; i < 2; i++) {
var x = margin.x + (i % 8) * margin.x;
var y = -200 + (i % 3) * margin.y;
this.addRepublic(new Alien(this, {x: x, y: y}, waveSpeed, waveProb));
}
};
Game.prototype.showText = function (message, final) {
this.message = message;
if (!final) setTimeout(this.showText.bind(this, '', true), Game.MESSAGE_DURATION);
};
/* GENERIC ELEMENT */
function Element (game, pos, size) {
this.game = game;
this.pos = pos;
this.size = size;
this.halfWidth = Math.floor(this.size.x / 2);
this.halfHeight = Math.floor(this.size.y / 2);
}
Element.update = function (element) {
element.update();
};
Element.draw = function (element) {
element.draw();
};
Element.isAlive = function (element) {
return element.life > 0;
};
Element.colliding = function (elementA, elementB) {
return !(
elementA === elementB ||
elementA.pos.x + elementA.halfWidth < elementB.pos.x - elementB.halfWidth ||
elementA.pos.y + elementA.halfHeight < elementB.pos.y - elementB.halfHeight ||
elementA.pos.x - elementA.halfWidth > elementB.pos.x + elementB.halfWidth ||
elementA.pos.y - elementA.halfHeight > elementB.pos.y + elementB.halfHeight
);
};
/* SHIP */
function Ship(game) {
var pos = {
x: Math.floor(game.size.x / 2) - Math.floor(Ship.SIZE.x / 2),
y: game.size.y - Math.floor(Ship.SIZE.y / 2)
};
Element.call(this, game, pos, Ship.SIZE);
this.kb = new KeyBoard();
this.speed = Ship.SPEED;
this.allowShooting = true;
this.life = Ship.MAX_LIFE;
this.fireRate = Ship.FIRE_RATE;
}
Ship.SIZE = {x: 67, y: 100};
Ship.SPEED = 8;
Ship.MAX_LIFE = 5;
Ship.FIRE_RATE = 200;
Ship.prototype.update = function () {
if (this.kb.isDown(KeyBoard.KEYS.LEFT) && this.pos.x - this.halfWidth > 0) {
this.pos.x -= this.speed;
} else if (this.kb.isDown(KeyBoard.KEYS.RIGHT) && this.pos.x + this.halfWidth < this.game.size.x) {
this.pos.x += this.speed;
}
if (this.allowShooting && this.kb.isDown(KeyBoard.KEYS.SPACE)) {
var bullet = new Bullet(
this.game,
{x: this.pos.x, y: this.pos.y - this.halfHeight },
{ x: 0, y: -Bullet.SPEED },
true
);
this.game.addRebel(bullet);
this.toogleShooting();
}
};
Ship.prototype.draw = function () {
var img = document.getElementById('ship');
context.save();
context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
context.drawImage(img, 0, 0);
context.restore();
this.drawLife();
};
Ship.prototype.drawLife = function () {
context.save();
context.fillStyle = 'white';
context.fillRect(this.game.size.x -112, 10, 102, 12);
context.fillStyle = 'red';
context.fillRect(this.game.size.x -111, 11, this.life * 100 / Ship.MAX_LIFE, 10);
context.restore();
};
Ship.prototype.toogleShooting = function (final) {
this.allowShooting = !this.allowShooting;
if (!final) setTimeout(this.toogleShooting.bind(this, true), this.fireRate);
};
/* ALIENS */
function Alien(game, pos, speed, shootProb) {
Element.call(this, game, pos, Alien.SIZE);
this.speed = speed;
this.shootProb = shootProb;
this.life = 3;
this.direction = {x: 1, y: 1};
}
Alien.SIZE = {x: 51, y: 60};
Alien.MAX_RANGE = 350;
Alien.CHDIR_PRO = 0.990;
Alien.drawLife = function (array) {
array = array.filter(function (element) {
return element instanceof Alien;
});
context.save();
context.fillStyle = 'white';
context.fillRect(10, 10, 10 * array.length + 2, 12);
array.forEach(function (alien, idx) {
switch (alien.life) {
case 3:
context.fillStyle = 'green';
break;
case 2:
context.fillStyle = 'yellow';
break;
case 1:
context.fillStyle = 'red';
break;
}
context.fillRect(10 * idx + 11, 11, 10, 10);
});
context.restore();
};
Alien.prototype.update = function () {
if (this.pos.x - this.halfWidth <= 0) {
this.direction.x = 1;
} else if (this.pos.x + this.halfWidth >= this.game.size.x) {
this.direction.x = -1;
} else if (Math.random() > Alien.CHDIR_PRO) {
this.direction.x = -this.direction.x;
}
if (this.pos.y - this.halfHeight <= 0) {
this.direction.y = 1;
} else if (this.pos.y + this.halfHeight >= Alien.MAX_RANGE) {
this.direction.y = -1;
} else if (Math.random() > Alien.CHDIR_PRO) {
this.direction.y = -this.direction.y;
}
this.pos.x += this.speed * this.direction.x;
this.pos.y += this.speed * this.direction.y;
if (Math.random() > this.shootProb) {
var bullet = new Bullet(
this.game,
{x: this.pos.x, y: this.pos.y + this.halfHeight },
{ x: Math.random() - 0.5, y: Bullet.SPEED },
false
);
this.game.addRepublic(bullet);
}
};
Alien.prototype.draw = function () {
var img = document.getElementById('fighter');
context.save();
context.translate(this.pos.x + this.halfWidth, this.pos.y + this.halfHeight);
context.rotate(Math.PI);
context.drawImage(img, 0, 0);
context.restore();
};
/* BULLET */
function Bullet(game, pos, direction, isRebel) {
Element.call(this, game, pos, Bullet.SIZE);
this.direction = direction;
this.isRebel = isRebel;
this.life = 1;
try {
var sound = document.getElementById('sound-raygun');
sound.load();
sound.play().then(function () {}, function () {});
}
catch (e) {
// only a sound issue
}
}
Bullet.SIZE = {x: 6, y: 20};
Bullet.SPEED = 3;
Bullet.prototype.update = function () {
this.pos.x += this.direction.x;
this.pos.y += this.direction.y;
};
Bullet.prototype.draw = function () {
context.save();
var img;
if (this.isRebel) {
context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
img = document.getElementById('rebel-bullet');
}
else {
context.translate(this.pos.x + this.halfWidth, this.pos.y + this.halfHeight);
img = document.getElementById('republic-bullet');
context.rotate(Math.PI);
}
context.drawImage(img, 0, 0);
context.restore();
};
/* EXPLOSION */
function Explosion(game, pos) {
Element.call(this, game, pos, Explosion.SIZE);
this.life = 1;
this.date = new Date();
try {
var sound = document.getElementById('sound-explosion');
sound.load();
sound.play().then(function () {}, function () {});
}
catch (e) {
// only a sound issue
}
}
Explosion.SIZE = {x: 115, y: 100};
Explosion.DURATION = 150;
Explosion.prototype.update = function () {
if (new Date() - this.date > Explosion.DURATION) this.life = 0;
};
Explosion.prototype.draw = function () {
var img = document.getElementById('explosion');
context.save();
context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
context.drawImage(img, 0, 0);
context.restore();
};
/* KEYBOARD HANDLING */
function KeyBoard() {
var state = {};
window.addEventListener('keydown', function(e) {
state[e.keyCode] = true;
});
window.addEventListener('keyup', function(e) {
state[e.keyCode] = false;
});
this.isDown = function (key) {
return state[key];
};
}
KeyBoard.KEYS = {
LEFT: 37,
RIGHT: 39,
SPACE: 32
};
window.addEventListener('load', function() {
game = new Game();
});
// Get all the button elements that are children of elements that have
// the .button-pad class and convert the resulting node list into an Array
let elements =
Array.prototype.slice.call(document.querySelectorAll('.button-pad button'));
// Loop over the array
elements.forEach(function(el){
el.textContent = "XXXX";
// Set up a click event handler for the current element being iterated:
el.addEventListener('click', function(e) {
// When the element is clicked, check to see if it uses the left-btn class
if(this.classList.contains('left-btn')) {
// Perform whatever actions you need to:
ship.update();
}
});
});
}
<h1>Gatwick invaders</h1>
<p>Press <b>left arrow</b> to go left, <b>right arrow</b> to go right, and <b>space</b> to shoot...</p>
<canvas id="space-invader" width="640" height="500" tabindex="0"></canvas>
<img id="fighter" src="https://raw.githubusercontent.com/MrVIncentRyan/assets/master/drone1.png" />
<img id="ship" src="https://raw.githubusercontent.com/MrVIncentRyan/assets/master/cop1.png" />
<img id="rebel-bullet" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/rebelBullet.png" />
<img id="republic-bullet" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/republicBullet.png" />
<img id="explosion" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/explosion.png" />
<audio id="sound-explosion" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/explosion.mp3"></audio>
<audio id="sound-raygun" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/raygun.mp3"></audio>
</div>
<div class="button-pad">
<div class="btn-up">
<button type="submit" class="up">
<img src="http://aaronblomberg.com/sites/ez/images/btn-up.png" />
</button>
</div>
<div class="btn-right">
<button type="submit" class="right">
<img src="http://aaronblomberg.com/sites/ez/images/btn-right.png" />
</button>
</div>
<div class="btn-down">
<button type="submit" class="down">
<img src="http://aaronblomberg.com/sites/ez/images/btn-down.png" />
</button>
</div>
<div class="btn-left">
<button type="submit" class="left">
<img src="http://aaronblomberg.com/sites/ez/images/btn-left.png" />
</button>
</div>
</div>
A custom solution for emulating keypresses on mobile in both vanilla Javascript as well as jQuery!
// jQuery (edge), for use with ES2015-19
/*
$(document).on("click", ".example-btn", e => { // Click event handler
if($(this).hasClass("example-btn")) { // Verifying that element has class
e = 37
jQuery.event.trigger({type: "keypress", which: character.charCodeAt(e)}) // Simulating keystroke
// The following is simply for debugging, remove if needed
alert("Button validation confirmed!")
console.log("E: ", e)
}
})
*/
// Pure Javascript (ECMA Standard)
document.querySelector(".example-btn").addEventListener("click", function(e) { // Click event handler
if(this.classList.contains("example-btn")) { // Verifying that element has class
e = 37
if(document.createEventObject) {
var eventObj = document.createEventObject();
eventObj.keyCode = e;
document.querySelector(".example-btn").fireEvent("onkeydown", eventObj);
} else if(document.createEvent) {
var eventObj2 = document.createEvent("Events");
eventObj2.initEvent("keydown", true, true);
eventObj2.which = e;
document.querySelector(".example-btn").dispatchEvent(eventObj2);
}
// The following is simply for debugging, remove if needed
alert("Button validation confirmed!");
console.log("E: ", e);
}
});
// ---------------------------------------------------------------------------------------------------
/*
You can not use the "this" statement when referring to an embedded element. In your previous code "this" would refer to ".button-container > .example-btn" which the compiler will interpret as only the parent element, being .button-container (.button-pad in your code) not the child element in which you want. Also there is no such thing as returning a character code and expecting it to automatically know what to do with it. I assume you are doing this to emulate a keystroke on a mobile device and I assure you that this design works although it might be flawed. Give it a try and I hope it does something to at least help if not solve your problem.
*/
// ---------------------------------------------------------------------------------------------------
When an event listener is attached to an element, that listener is not unique for the element, but it propagates to its children.
This functionality is enabled in jQuery by adding a parameter on an event listener a parameter that targets the element that we want.
This is not case in vanillaJS, but using e.target we can inspect in which elements the event is executed.
Probably your are looking something like this. However, I would prefer to add an id in the button so you can more easily work with it.
document.addEventListener('click', function(e){
if(e.target.tagName === 'BUTTON' && e.target.classList.value.includes('btn-left')){
// execute your code
}
});
Trying to create a slider class that lets you easily and quickly customize values. You can drag the handle or click on any part of the slider to move the handle there, and it works... sort of. You can click as many times as you want, but... if you even click once or if you drag, then let go and try again, it only goes down (never up) a tiny bit. That probably sounds confusing, so here's the fiddle.
var Slider = createEntity({
init: function (args) {
args = args || {};
this.x = args.x || 0;
this.y = args.y || 0;
this.width = args.width || 10;
this.height = args.height || 100;
this.min = args.min || 0;
this.max = args.max || 100;
this.value = args.value || 50;
this.rotation = args.rotation || 0;
this.on = args.on || function () {};
var backFill = randHsla();
args.back = args.back || {};
this.back = args.back;
args.back.fill = args.back.fill || backFill;
this.back.fill = args.back.fill;
args.back.borderFill = args.back.borderFill || backFill;
this.back.borderFill = args.back.borderFill;
args.back.width = args.back.width || 5;
this.back.width = args.back.width;
args.back.x = args.back.x || this.width / 2 - this.back.width / 2;
this.back.x = args.back.x;
var handleColor = randHsla();
args.handle = args.handle || {};
this.handle = args.handle;
args.handle.fill = args.handle.fill || handleColor;
this.handle.fill = args.handle.fill;
args.handle.borderStroke = args.handle.borderStroke || handleColor;
this.handle.borderStroke = args.handle.borderStroke;
args.handle.height = args.handle.height || 5;
this.handle.height = args.handle.height;
args.handle.y = args.handle.y || 0;
this.handle.y = args.handle.y;
this.updatePos();
},
draw: function (fx) {
fx.save();
fx.translate(this.x, this.y);
fx.rotate(this.rotation);
fx.fillStyle = this.back.fill;
fx.beginPath();
fx.fillRect(this.back.x, 0, this.back.width, this.height);
fx.closePath();
fx.fillStyle = this.handle.fill;
fx.strokeStyle = this.handle.borderStroke;
fx.beginPath();
fx.rect(0, this.handle.y, this.width, this.handle.height);
fx.closePath();
fx.fill();
fx.stroke();
fx.restore();
},
updateVal: function () {
var oldVal = this.value,
handleRange = this.height - this.handle.height,
valRange = this.max - this.min;
this.value = (handleRange - this.handle.y) / handleRange * valRange + this.min;
if (this.on instanceof Function && this.value !== oldVal) {
this.on();
}
return this;
},
updatePos: function () {
var handleRange = this.height - this.handle.height,
valRange = this.max - this.min;
this.handle.y = handleRange - ((this.value - this.min) / valRange) * handleRange;
return this;
},
getMouse: function (map) {
var self = this,
mouse = getMouse(map),
bounds = {};
setBounds();
map.addEventListener('mousedown', function (event) {
if (hasPoint(bounds, mouse.x, mouse.y)) {
map.addEventListener('mousemove', onMouseMove);
map.addEventListener('mouseup', onMouseUp);
} else if (hasPoint(self, mouse.x, mouse.y)) {
var y = mouse.y - self.y;
self.handle.y = Math.min(self.height - self.handle.height, Math.max(y, 0));
self.updateVal();
}
});
function onMouseUp(event) {
map.removeEventListener('mousemove', onMouseMove, false);
map.removeEventListener('mouseup', onMouseUp, false);
}
function onMouseMove(event) {
var y = mouse.y - self.y;
self.handle.y = Math.min(self.height - self.handle.height, Math.max(y, 0));
self.updateVal();
}
function setBounds() {
bounds.x = self.x;
bounds.y = self.y + self.handle.y;
bounds.width = self.width;
bounds.height = self.handle.height;
}
return this;
}
});
External functions such as createEntity and hasPoint can be found here.
How would I make it work after clicking the slider and letting go after the first?
Add the event listeners if the user clicks on the handle as well, to enable dragging.
map.addEventListener('mousedown', function(event) {
if (hasPoint(bounds, mouse.x, mouse.y)) {
map.addEventListener('mousemove', onMouseMove);
map.addEventListener('mouseup', onMouseUp);
} else if (hasPoint(self, mouse.x, mouse.y)) {
map.addEventListener('mousemove', onMouseMove);
map.addEventListener('mouseup', onMouseUp);
var y = mouse.y - self.y;
self.handle.y = Math.min(self.height - self.handle.height, Math.max(y, 0));
self.updateVal();
}
});
I am trying to implement custom draggable directive in angularjs but it trows
Range error . what is wrong in this code
(function (window, angular, undefined) {
var app = angular.module('ngDraggableModule', []);
app.directive('easyDrag', ['$document', function ($document) {
return {
restrict: 'EA',
scope: {
params: '=',
elemId: '='
},
link: function (scope, elem, attrs) {
var isMouseDown = false,
startDrag = false,
position = {},
x = 0,
y = 0,
startX = 0,
startY = 0,
changedPos = 0;
elem.bind('mousedown', onMouseDown);
function onMouseDown(e) {
if (angular.isDefined(scope.params.disabled) && scope.params.disabled === true) {
return false;
}
isMouseDown = true;
startX = e.screenX - x;
startY = e.screenY - y;
$document.bind('mousemove', onMouseMove);
$document.bind('mouseup', onMouseUp);
}
function onMouseMove(e) {
if (isMouseDown) {
if (!startDrag) {
startDrag = true;
if (angular.isFunction(scope.params.start)) {
scope.params.start(changedPos, e, scope.elemId);
}
}
y = e.screenY - startY;
x = e.screenX - startX;
position = {};
if (angular.isDefined(scope.params.axis)) {
if (scope.params.axis.toLowerCase() == 'x') {
position.marginLeft = x + 'px';
}
else if (scope.params.axis.toLowerCase() == 'y') {
position.marginTop = y + 'px';
}
} else {
position.marginTop = y + 'px';
position.marginLeft = x + 'px';
}
changedPos = position;
elem.css(position);
if (angular.isFunction(scope.params.drag)) {
scope.params.drag(e, changedPos, scope.elemId);
}
}
}
function onMouseUp(e) {
if (!isMouseDown) {
return false;
}
isMouseDown = false;
startDrag = false;
if (angular.isFunction(scope.params.stop)) {
scope.params.stop(changedPos, e, scope.elemId);
}
}
}
}
}]);
})(window, window.angular, undefined);
var app = angular.module('DemoApp',['ngDraggableModule']);
app.controller('MainCtrl',['$scope',function($scope){
$scope.assetDragParams = {
drag : function(e,pos,id){
console.log(id); // here is the error
}
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="MainCtrl">
<div easy-drag params="assetDragParams" elem-id="col._id">
drag it
</div>
</div>
calling directive such this trows RangeError : Maximum call stack size exceeded
what is wrong ? thanks in advance.
You need to bind the mousemove + mouseup outside of mousedown, anyway you set isMouseDown = true in the onMouseDown function. All the function binding should be like this:
elem.bind('mousedown', onMouseDown);
$document.bind('mousemove', onMouseMove);
$document.bind('mouseup', onMouseUp);
And onMouseDown function should be:
function onMouseDown(e) {
if (angular.isDefined(scope.params.disabled) && scope.params.disabled === true) {
return false;
}
isMouseDown = true;
startX = e.screenX - x;
startY = e.screenY - y;
}
I have a circle that has a menu with objects positioned in a circle inside the circle. When the user drags their finger in any direction the circle spins along with the finger, however, when the user is spinning the wheel and happens to touch one of the objects inside (serves as a link) the circle "brakes" and stops spinning. I need to cancel the event bubble. I know I need to use event.stopPropagation and deal with the capture inside the event handler but I am having issues implementing this with a jQuery plugin (Touchy) that I found.
*MAKE SURE YOU ARE VIEWING IN CHROME WITH THE OVERRIDES SET TO EMULATE TOUCH EVENTS AND THAT THE DEVELOPER CONSOLE IS OPEN Command+Option+i on Mac!!! *
http://jsfiddle.net/ymtV3/
*MAKE SURE YOU ARE VIEWING IN CHROME WITH THE OVERRIDES SET TO EMULATE TOUCH EVENTS AND THAT THE DEVELOPER CONSOLE IS OPEN Command+Option+i on Mac!!! *
<div id="wheelMenu">
<div id="wheel">
<ul class="items">
<li class="flashOff"><span>Flash</span></li>
<li class="sceneOff"><span>Scene</span></li>
<li class="hdrOff"><span>Hdr</span></li>
<li class="panoramaOff"><span>Pana</span></li>
<li class="resolutionOff"><span>Resolu</span></li>
<li class="reviewOff"><span>Review</span></li>
<li class="continuousShootingOff"><span>Contin</span></li>
<li class="dropBoxOff"><span>DropBox</span></li>
<li class="timerOff"><span>Timer</span></li>
</ul>
</div>
</div>
(function ($) {
$.touchyOptions = {
useDelegation: false,
rotate: {
preventDefault: {
start: true,
move: true,
end: true
},
stopPropagation: {
start: true,
move: true,
end: true
},
requiredTouches: 1,
data: {},
proxyEvents: ["TouchStart", "TouchMove", "GestureChange", "TouchEnd"]
}
};
var proxyHandlers = {
handleTouchStart: function (e) {
var eventType = this.context,
$target = getTarget(e, eventType);
if ($target) {
var event = e.originalEvent,
touches = event.targetTouches,
camelDataName = 'touchy' + eventType.charAt(0).toUpperCase() + eventType.slice(1),
data = $target.data(camelDataName),
settings = data.settings;
if (settings.preventDefault.start) {
event.preventDefault();
}
if (settings.stopPropagation.start) {
event.stopPropagation();
}
if (touches.length === settings.requiredTouches) {
switch (eventType) {
case 'rotate':
if (touches.length === 1) {
ensureSingularStartData(data, touches, e.timeStamp);
console.log(eventType);
console.log(touches);
console.log(data);
} else {
var points = getTwoTouchPointData(e);
data.startPoint = {
"x": points.centerX,
"y": points.centerY
};
data.startDate = e.timeStamp;
}
var startPoint = data.startPoint;
$target.trigger('touchy-rotate', ['start', $target, {
"startPoint": startPoint,
"movePoint": startPoint,
"lastMovePoint": startPoint,
"velocity": 0,
"degrees": 0
}]);
break;
}
}
}
},
handleTouchMove: function (e) {
var eventType = this.context,
$target = getTarget(e, eventType);
if ($target) {
var event = e.originalEvent,
touches = event.targetTouches,
camelDataName = 'touchy' + eventType.charAt(0).toUpperCase() + eventType.slice(1),
data = $target.data(camelDataName),
settings = data.settings;
if (settings.preventDefault.move) {
event.preventDefault();
}
if (settings.stopPropagation.move) {
event.stopPropagation();
}
if (touches.length === settings.requiredTouches) {
switch (eventType) {
case 'rotate':
var lastMovePoint,
lastMoveDate,
movePoint,
moveDate,
lastMoveDate,
distance,
ms,
velocity,
targetPageCoords,
centerCoords,
radians,
degrees,
lastDegrees,
degreeDelta;
lastMovePoint = data.lastMovePoint = data.movePoint || data.startPoint;
lastMoveDate = data.lastMoveDate = data.moveDate || data.startDate;
movePoint = data.movePoint = {
"x": touches[0].pageX,
"y": touches[0].pageY
};
moveDate = data.moveDate = e.timeStamp;
if (touches.length === 1) {
targetPageCoords = data.targetPageCoords = data.targetPageCoords || getViewOffset(e.target);
centerCoords = data.centerCoords = data.centerCoords || {
"x": targetPageCoords.x + ($target.width() * 0.5),
"y": targetPageCoords.y + ($target.height() * 0.5)
};
} else {
var points = getTwoTouchPointData(e);
centerCoords = data.centerCoords = {
"x": points.centerX,
"y": points.centerY
};
if (hasGestureChange()) {
break;
}
}
radians = Math.atan2(movePoint.y - centerCoords.y, movePoint.x - centerCoords.x);
lastDegrees = data.lastDegrees = data.degrees;
degrees = data.degrees = radians * (180 / Math.PI);
degreeDelta = lastDegrees ? degrees - lastDegrees : 0;
ms = moveDate - lastMoveDate;
velocity = data.velocity = ms === 0 ? 0 : degreeDelta / ms;
$target.trigger('touchy-rotate', ['move', $target, {
"startPoint": data.startPoint,
"startDate": data.startDate,
"movePoint": movePoint,
"lastMovePoint": lastMovePoint,
"centerCoords": centerCoords,
"degrees": degrees,
"degreeDelta": degreeDelta,
"velocity": velocity
}]);
break;
}
}
}
},
handleGestureChange: function (e) {
var eventType = this.context,
$target = getTarget(e, eventType);
if ($target) {
var $target = $(e.target),
event = e.originalEvent,
touches = event.touches,
camelDataName = 'touchy' + eventType.charAt(0).toUpperCase() + eventType.slice(1),
data = $target.data(camelDataName);
if (data.preventDefault.move) {
event.preventDefault();
}
if (settings.stopPropagation.move) {
event.stopPropagation();
}
switch (eventType) {
case 'rotate':
var lastDegrees = data.lastDegrees = data.degrees,
degrees = data.degrees = event.rotation,
degreeDelta = lastDegrees ? degrees - lastDegrees : 0,
ms = data.moveDate - data.lastMoveDate,
velocity = data.velocity = ms === 0 ? 0 : degreeDelta / ms;
$target.trigger('touchy-rotate', ['move', $target, {
"startPoint": data.startPoint,
"startDate": data.startDate,
"movePoint": data.movePoint,
"lastMovePoint": data.lastMovePoint,
"centerCoords": data.centerCoords,
"degrees": degrees,
"degreeDelta": degreeDelta,
"velocity": velocity
}]);
break;
}
}
},
handleTouchEnd: function (e) {
var eventType = this.context,
$target = getTarget(e, eventType);
if ($target) {
var event = e.originalEvent,
camelDataName = 'touchy' + eventType.charAt(0).toUpperCase() + eventType.slice(1),
data = $target.data(camelDataName),
settings = data.settings;
if (settings.preventDefault.end) {
event.preventDefault();
}
if (settings.stopPropagation.end) {
event.stopPropagation();
}
switch (eventType) {
case 'rotate':
var degreeDelta = data.lastDegrees ? data.degrees - data.lastDegrees : 0;
$target.trigger('touchy-rotate', ['end', $target, {
"startPoint": data.startPoint,
"startDate": data.startDate,
"movePoint": data.movePoint,
"lastMovePoint": data.lastMovePoint,
"degrees": data.degrees,
"degreeDelta": degreeDelta,
"velocity": data.velocity
}]);
$.extend(data, {
"startPoint": null,
"startDate": null,
"movePoint": null,
"moveDate": null,
"lastMovePoint": null,
"lastMoveDate": null,
"targetPageCoords": null,
"centerCoords": null,
"degrees": null,
"lastDegrees": null,
"velocity": null
});
break;
}
}
}
},
ensureSingularStartData = function (data, touches, timeStamp) {
if (!data.startPoint) {
data.startPoint = {
"x": touches[0].pageX,
"y": touches[0].pageY
}
}
if (!data.startDate) {
data.startDate = timeStamp;
}
},
hasGestureChange = function () {
return (typeof window.ongesturechange == "object");
},
getTarget = function (e, eventType) {
var $delegate,
$target = false,
i = 0,
len = boundElems[eventType].length
if ($.touchyOptions.useDelegation) {
for (; i < len; i += 1) {
$delegate = $(boundElems[eventType][i]).has(e.target);
if ($delegate.length > 0) {
$target = $delegate;
break;
}
}
} else if (boundElems[eventType] && boundElems[eventType].index(e.target) != -1) {
$target = $(e.target)
}
return $target;
},
getViewOffset = function (node, singleFrame) {
function addOffset(node, coords, view) {
var p = node.offsetParent;
coords.x += node.offsetLeft - (p ? p.scrollLeft : 0);
coords.y += node.offsetTop - (p ? p.scrollTop : 0);
if (p) {
if (p.nodeType == 1) {
var parentStyle = view.getComputedStyle(p, '');
if (parentStyle.position != 'static') {
coords.x += parseInt(parentStyle.borderLeftWidth);
coords.y += parseInt(parentStyle.borderTopWidth);
if (p.localName == 'TABLE') {
coords.x += parseInt(parentStyle.paddingLeft);
coords.y += parseInt(parentStyle.paddingTop);
} else if (p.localName == 'BODY') {
var style = view.getComputedStyle(node, '');
coords.x += parseInt(style.marginLeft);
coords.y += parseInt(style.marginTop);
}
} else if (p.localName == 'BODY') {
coords.x += parseInt(parentStyle.borderLeftWidth);
coords.y += parseInt(parentStyle.borderTopWidth);
}
var parent = node.parentNode;
while (p != parent) {
coords.x -= parent.scrollLeft;
coords.y -= parent.scrollTop;
parent = parent.parentNode;
}
addOffset(p, coords, view);
}
} else {
if (node.localName == 'BODY') {
var style = view.getComputedStyle(node, '');
coords.x += parseInt(style.borderLeftWidth);
coords.y += parseInt(style.borderTopWidth);
var htmlStyle = view.getComputedStyle(node.parentNode, '');
coords.x -= parseInt(htmlStyle.paddingLeft);
coords.y -= parseInt(htmlStyle.paddingTop);
}
if (node.scrollLeft) coords.x += node.scrollLeft;
if (node.scrollTop) coords.y += node.scrollTop;
var win = node.ownerDocument.defaultView;
if (win && (!singleFrame && win.frameElement)) addOffset(win.frameElement, coords, win);
}
}
......seee the rest on the JS Fiddle