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
Related
I've created a loop in Javascript that shows a new image every 2 seconds.
Additionally, it shows a new image also when the user move the mouse, but this is not an issue.
The problem is that the CPU usage is not as low as I would expect: 7-10% on PC and if I open the website on my mobile it slows down too much...
Other things to say are that this is part of a Nuxt 3 project and I tried both to put this in a plugin or under a component script section with the same result.
Here is the code part of the main "loop".
Initially I fetch the images from the backend and keep them in img elements that I then clone for better performance.
Then it starts looping with "window.requestAnimationFrame".
In order to have some improvements, I generate an arbitrary amount of images (30 if mobile, 60 on desktops) and then I just reuse them.
import { Point, AnchorWork } from './extra/types';
import * as utils from './extra/utils';
export default defineNuxtPlugin(async () => {
async function start() {
const { find } = useStrapi(),
apiUrl = useApi(),
isMobile = useMobile(),
baseLink = document.createElement('a');
try {
let loadedImages = 0;
const pics = await find('works', { populate: '*' }),
rawImages = [],
totalImages = Object.keys(pics.data).length;
pics.data.forEach((pic) => {
const picUrl = apiUrl + (isMobile ? pic.attributes.image.data.attributes.formats.medium.url : pic.attributes.image.data.attributes.formats.xlarge.url);
const img = new Image();
img.addEventListener('load', async () => {
const link = baseLink.cloneNode();
link.appendChild(img);
rawImages.push(link);
if (++loadedImages >= totalImages) {
utils.delay(100).then(async () => {
//document.getElementById('loader').style.opacity = '0';
await loop(rawImages, totalImages);
});
}
});
img.src = picUrl;
img.alt = pic.attributes.name;
})
} catch(error) {
console.log(error);
return;
}
}
await start();
async function loop(pictures, max) {
const isMobile = useMobile(),
maxQueue = isMobile ? 30 : 60,
imagesQueue = [],
delay = 2000,
baseDiv = document.createElement('div'),
body = document.body;
let auto = true,
picsContainer = document.getElementById('pictures'),
mainContainer = document.getElementById('main'),
mouseMinDelta = window.innerWidth * 0.1 + window.innerHeight * 0.1,
current = Math.floor( Math.random() * ( max - 3 ) ),
mouseTimeout,
resizeTimeout,
oldMouse = new Point(0, 0),
movement = new Point(0, 0);
async function generateImg(px, py) {
const { x, y } = px && py ? { x: px, y: py } : utils.generateCoord(picsContainer.clientWidth, picsContainer.clientHeight);
if(imagesQueue.length < maxQueue) {
if(current >= max) current = 0;
const link = new AnchorWork(pictures[current++].cloneNode(true), x, y);
await link.appendTo(picsContainer);
let overlay = baseDiv.cloneNode();
link.element.addEventListener('click', (e) => {
e.preventDefault();
workClick(link, overlay);
});
imagesQueue.push(link);
} else {
const index = imagesQueue[0].hasClass('active') ? 1 : 0;
const item = imagesQueue[index];
await item.removeClass('work');
item.position = new Point(x, y);
picsContainer.lastElementChild.after(item.element);
item.newSize();
imagesQueue.push(imagesQueue.splice(index, 1)[0]);
setTimeout(() => { item.addClass('work'); }, 200);
}
}
function workClick(link, overlay) {
const target = link;
if(target.hasClass('active')) {
target.childImage.resetStyle();
overlay.remove();
target.removeClass('active');
} else {
const height = body.clientHeight > body.clientWidth ? 'auto' : '100%';
const width = height == 'auto' ? '100%' : 'auto';
target.childImage.modStyle('height: ' + height + '; width: ' + width + ';');
overlay.classList.add('overlay');
overlay.addEventListener('click', overlay.remove);
picsContainer.appendChild(overlay);
target.appendTo(mainContainer);
target.addClass('active');
}
}
if(!isMobile) {
picsContainer.addEventListener('mousemove', (event) => {
clearTimeout(mouseTimeout);
mouseTimeout = setTimeout(() => {
picsContainer.dispatchEvent(new Event('mousestop'))
}, delay);
movement.x += Math.abs(event.pageX - oldMouse.x);
movement.y += Math.abs(event.pageY - oldMouse.y);
if(movement.x + movement.y > mouseMinDelta) {
generateImg(picsContainer.clientWidth - event.pageX, picsContainer.clientHeight - event.pageY);
movement.Set(0, 0);
}
oldMouse.Set(event.pageX, event.pageY);
auto = false;
});
picsContainer.addEventListener('mousestop', () => { auto = true; });
}
//Reposition images based on new window size
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
window.dispatchEvent(new Event('resizeend'))
}, 20);
});
window.addEventListener('resizeend', () => {
mouseMinDelta = window.innerWidth * 0.1 + window.innerHeight * 0.1;
imagesQueue.forEach(img => {
const coord = utils.generateCoord(picsContainer.clientWidth, picsContainer.clientHeight),
point = new Point(coord.x, coord.y);
if(!img.hasClass('active')) {
img.position = point;
img.newSize();
}
})
})
//Initial images
for(let x = 0; x < 3; x++) { generateImg(); }
async function animate() {
if(auto) {
generateImg();
}
await utils.delay(delay);
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
}
});
Here are some classes I created to help me:
import * as utils from './utils';
class BaseWork {
_width;
_height;
_style;
_element;
constructor(el) {
this._element = el;
const size = utils.generateSize();
this._width = size.width;
this._height = size.height;
this._style = '';
}
//Getters - Setters
get element() { return this._element; }
get width() { return this._width; }
get height() { return this._height; }
get style() { return this._style; }
set width(newWidth) { this._width = newWidth; }
set height(newHeight) { this._height = newHeight; }
set style(newStyle) {
this._style = newStyle;
this.element.setAttribute('style', newStyle);
}
//Methods
async addClass(className) { this.element.classList.add(className); }
async removeClass(className) { this.element.classList.remove(className); }
hasClass(className) { return this.element.classList.contains(className); }
async appendTo(container) {
container.appendChild(this.element);
await utils.delay(20); //To let the rect being computed
}
async resetStyle() { this.element.setAttribute('style', this.style); }
async modStyle(newStyle) { this.element.setAttribute('style', newStyle); }
}
class ImageWork extends BaseWork {
_container;
constructor(el, container) {
super(el);
this._container = container;
this.newSize();
}
get element() { return this._element; }
get container() { return this._container; }
async newSize() {
this.width = this.container.element.clientHeight > this.container.element.clientWidth ? 'auto' : '100%';
this.height = this.width == 'auto' ? '100%' : 'auto';
this.style = 'width: ' + this.width + '; height: ' + this.height + ';';
}
}
class AnchorWork extends BaseWork {
_position = new Point(0, 0);
_childImage = null;
constructor( el, x = 0, y = 0) {
super(el);
if(x == 0 || y == 0) {
const coord = utils.generateCoord(window.innerWidth, window.innerHeight);
this._position.Set(coord.x, coord.y);
} else this._position.Set(x, y);
this.style = 'left: ' + this.position.x + 'px; top: ' + this.position.y + 'px; display: none; width: ' + this.width + '; height: ' + this.height + ';';
this.generateImage(this.element.querySelector('img'));
setTimeout(() => { this.addClass('work'); }, 200);
}
//Getters - Setters
get element() { return this._element; }
get position() { return this._position; }
get childImage() { return this._childImage; }
set position(newPosition) {
this._position = newPosition;
this.style = 'left: ' + newPosition.x + 'px; top: ' + newPosition.y + 'px; display: none; width: ' + this.width + '; height: ' + this.height + ';';
}
//Methods
generateImage(el) { this._childImage = new ImageWork(el, this); }
async newSize() {
const size = utils.generateSize();
this.width = size.width;
this.height = size.height;
this.style = 'left: ' + this.position.x + 'px; top: ' + this.position.y + 'px; display: none; width: ' + this.width + '; height: ' + this.height + ';';
this.childImage.newSize();
}
}
class Point {
_x;
_y;
constructor(x = 0, y = 0) {
this._x = x;
this._y = y;
}
get x() { return this._x; }
get y() { return this._y; }
set x(newX) { this._x = newX; }
set y(newY) { this._y = newY; }
Set(x, y) {
this.x = x;
this.y = y;
}
}
export { BaseWork, ImageWork, AnchorWork, Point }
Here are some helper functions:
function generateSize() {
const mod = document.body.clientHeight > document.body.clientWidth ? true : false;
const modW = (mod ? 40 : 20);
const modH = (mod ? 20 : 40);
return { width: Math.round((Math.random() * modW) + modW) + '%', height: Math.round((Math.random() * modH) + modH) + '%' }
}
function generateCoord(maxX, maxY) {
let x = Math.round(Math.random() * (maxX ? maxX : window.innerWidth));
let y = Math.round(Math.random() * (maxY ? maxY : window.innerHeight));
return { x: x, y: y };
}
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
export { generateCoord, generateSize, delay }
I'm having issues when trying to add an event listener to a div in a function.
function cancelMove()
{
alert("cancelMove called");
}
function createTile ( posX, posY, tileColor, id )
{
var tile = document.createElement ( "tile" )
tile.style.left = posX + "px"
tile.style.position = "absolute"
tile.style.top = posY + "px"
tile.style.width = "75px"
tile.style.height = "75px"
tile.id = "tile" + id.toString()
tile.addEventListener("click", () => cancelMove() );
if ( tileColor%2 == 0 )
{
tile.style.backgroundColor = "#ebc5c5"
}
else
{
tile.style.backgroundColor = "#1a4018"
}
document.body.append(tile)
}
createTile(50, 60, 1, "id");
I'm trying to add an event listener that runs when any of the tiles are clicked. When I take the code out of the function, it works fine. Any help would be much appreciated.
Your code is working for me. Try this snippet.
<body></body>
<script>
function cancelMove() {
alert('cancelMove called');
}
function createTile(posX, posY, tileColor, id) {
var tile = document.createElement('tile');
tile.style.left = posX + 'px';
tile.style.position = 'absolute';
tile.style.top = posY + 'px';
tile.style.width = '75px';
tile.style.height = '75px';
tile.id = 'tile' + id.toString();
tile.addEventListener('click', () => cancelMove());
if (tileColor % 2 == 0) {
tile.style.backgroundColor = '#ebc5c5';
} else {
tile.style.backgroundColor = '#1a4018';
}
document.body.append(tile);
}
createTile(50, 60, 1, 'id');
</script>
I'm trying to follow along with this article to make an item move anywhere on the screen by dragging it: https://www.kirupa.com/html5/drag.htm The thing is that the article is in regular JavaScript and I'm using Vue so the way event handlers get handled are slightly different. For some reason, #dragStart and #drag don't fire at all but #dragEnd does fire when the user stops dragging the item.
Only div that I'm using for this
<div class="pip" draggable="true" #dragStart="dragStart" #drag="drag" #dragend="dragEnd">
Script tag
data() {
return {
active: false,
currentX: null,
currentY: null,
initialX: null,
initialY: null,
xOffset: 0,
yOffset: 0,
};
},
methods: {
dragStart(event) {
console.log('start');
const dragItem = document.querySelector('.pip');
if (event.type === 'touchstart') {
this.initialX = event.touches[0].clientX - this.xOffset;
this.initialY = event.touches[0].clientY - this.yOffset;
} else {
this.initialX = event.clientX - this.xOffset;
this.initialY = event.clientY - this.yOffset;
}
if (event.target === dragItem) {
this.active = true;
}
},
drag(event) {
console.log('drag');
const dragItem = document.querySelector('.pip');
if (this.active) {
event.preventDefault();
if (event.type === 'touchmove') {
this.currentX = event.touches[0].clientX - this.initialX;
this.currentY = event.touches[0].clientY - this.initialY;
} else {
this.currentX = event.clientX - this.initialX;
this.currentY = event.clientY - this.initialY;
}
this.xOffset = this.currentX;
this.yOffset = this.currentY;
this.setTranslate(this.currentX, this.currentY, dragItem);
}
},
dragEnd() {
console.log('end');
this.initialX = this.currentX;
this.initialY = this.currentY;
this.active = false;
// event.target.style.bottom = '0px';
// event.target.style.top = `${event.clientY - event.target.offsetHeight / 2}px`;
// event.target.style.left = `${event.clientX - event.target.offsetWidth / 2}px`;
// console.log(event);
},
setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(' + xPos + 'px, ' + yPos + 'px, 0)";
},
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
}
});
I am new to jquery. I have found this code for getting touch events. but it shows this. I don't know how to solve this problem.
error message
Uncaught SyntaxError: Unexpected token < in dev_touch.js
My code :
dev_touch.js
;(function($) {
'use strict'
var $html = $('html');
mainBody = main.find('body');
main.addClass("dev_toucher");
mainBody.append("<div></div>").attr("class", "dev_ui_toucher");
if (typeof $html.createEvent !== 'function') return false // no tap events here
// helpers
var useJquery = typeof jQuery !== 'undefined',
msPointerEnabled = !!navigator.pointerEnabled || navigator.msPointerEnabled,
isTouch = (!!('ontouchstart' in win) && navigator.userAgent.indexOf('PhantomJS') < 0) || msPointerEnabled,
msEventType = function(type) {
var lo = type.toLowerCase(),
ms = 'MS' + type
return navigator.msPointerEnabled ? ms : lo
},
touchevents = {
touchstart: msEventType('PointerDown') + ' touchstart',
touchend: msEventType('PointerUp') + ' touchend',
touchmove: msEventType('PointerMove') + ' touchmove'
},
setListener = function(elm, events, callback) {
var eventsArray = events.split(' '),
i = eventsArray.length
while (i--) {
elm.addEventListener(eventsArray[i], callback, false)
}
},
getPointerEvent = function(event) {
return event.targetTouches ? event.targetTouches[0] : event
},
getTimestamp = function () {
return new Date().getTime()
},
sendEvent = function(elm, eventName, originalEvent, data) {
var customEvent = $html.createEvent('Event')
customEvent.originalEvent = originalEvent
data = data || {}
data.x = currX
data.y = currY
data.distance = data.distance
// jquery
if (useJquery) {
customEvent = $.Event(eventName, {originalEvent: originalEvent})
jQuery(elm).trigger(customEvent, data)
}
// addEventListener
if (customEvent.initEvent) {
for (var key in data) {
customEvent[key] = data[key]
}
customEvent.initEvent(eventName, true, true)
elm.dispatchEvent(customEvent)
}
// inline
if (elm['on' + eventName])
elm['on' + eventName](customEvent)
},
onTouchStart = function(e) {
var pointer = getPointerEvent(e)
// caching the current x
cachedX = currX = pointer.pageX
// caching the current y
cachedY = currY = pointer.pageY
timestamp = getTimestamp()
tapNum++
// we will use these variables on the touchend events
},
onTouchEnd = function(e) {
var eventsArr = [],
now = getTimestamp(),
deltaY = cachedY - currY,
deltaX = cachedX - currX
// clear the previous timer in case it was set
clearTimeout(tapTimer)
if (deltaX <= -swipeThreshold)
eventsArr.push('swiperight')
if (deltaX >= swipeThreshold)
eventsArr.push('swipeleft')
if (deltaY <= -swipeThreshold)
eventsArr.push('swipedown')
if (deltaY >= swipeThreshold)
eventsArr.push('swipeup')
if (eventsArr.length) {
for (var i = 0; i < eventsArr.length; i++) {
var eventName = eventsArr[i]
sendEvent(e.target, eventName, e, {
distance: {
x: Math.abs(deltaX),
y: Math.abs(deltaY)
}
})
}
} else {
if (
cachedX >= currX - tapPrecision &&
cachedX <= currX + tapPrecision &&
cachedY >= currY - tapPrecision &&
cachedY <= currY + tapPrecision
){
if((timestamp + tapThreshold) - now >= 0){
// Here you get the Tap event
sendEvent(e.target, (tapNum === 2) && (target === e.target) ? 'dbltap' : 'tap', e)
target= e.target
}
else if((timestamp + longtapThreshold) - now <= 0){
// Here you get the Tap event
sendEvent(e.target,'longtap', e)
target= e.target
}
}
// reset the tap counter
tapTimer = setTimeout(function() {
tapNum = 0
}, dbltapThreshold)
}
},
onTouchMove = function(e) {
var pointer = getPointerEvent(e)
currX = pointer.pageX
currY = pointer.pageY
},
swipeThreshold = win.SWIPE_THRESHOLD || 100,
tapThreshold = win.TAP_THRESHOLD || 150, // range of time where a tap event could be detected
dbltapThreshold = win.DBL_TAP_THRESHOLD || 200, // delay needed to detect a double tap
longtapThreshold = win.LONG_TAP_THRESHOLD || 1000, // delay needed to detect a long tap
tapPrecision = win.TAP_PRECISION / 2 || 60 / 2, // touch events boundaries ( 60px by default )
justTouchEvents = win.JUST_ON_TOUCH_DEVICES || isTouch,
tapNum = 0,
currX, currY, cachedX, cachedY, tapTimer, timestamp, target
//setting the events listeners
setListener($html, touchevents.touchstart + (justTouchEvents ? '' : ' mousedown'), onTouchStart)
setListener($html, touchevents.touchend + (justTouchEvents ? '' : ' mouseup'), onTouchEnd)
setListener($html, touchevents.touchmove + (justTouchEvents ? '' : ' mousemove'), onTouchMove)
//test.on('tap',updateHtml);
//test.on('dbltap',updateHtml);
//test.on('longtap',updateHtml);
//test.on('swipeup',updateHtml);
//test.on('swipedown',updateHtml);
//test.on('swipeleft',updateHtml);
//test.on('swiperight',updateHtml);
}(document, window))
Can anyone help ?
I would be willing to be that you're being served an HTML file instead of your JavaScript file. Here's how to check:
Right-click anywhere on the page
Select 'Inspect Element'
Find your JS file and examine the contents.
See the picture for a pictorial representation with Firefox.