I’m doing a simple JS 2D board game for learning purposes. And I’m having trouble making sure that the player’s movements which are kicked off by arrow-keyboard-inputs get properly tracked in the console.
Supposing, I just want to limit myself to tracking any movement using the left arrow key:
then below there is the corresponding constructor function used for pre-defining the player objects that make their moves on the board (as I said, only to the left, for the time being:
function Player(name, posY, posX, currentTurn, numberMoves, weapon, previousWeapon) {
this.name = name;
this.posY = posY;
this.posX = posX;
this.currentTurn = currentTurn;
this.numberMoves = numberMoves; // counting the number of moves player has made.
this.weapon = weapon;
this.locatePosition = function() {
var l = 0;
var m = 0;
for (l = 0; l < mapArray.length; l++) {
for (m = 0; m < mapArray.length; m++) {
if ((mapArray[l][m]) === this.number) {
this.posY = [l];
this.posX = [m];
console.log("Player Position Y: " + this.posY + " + position X: " + this.posX);
}
}
}
}
//
this.movement = function() {
document.onkeydown = (e) => {
switch (e.which) {
case 37: // left - 1
this.numberMoves += 1;
console.log("player's number of moves made:" + this.numberMoves);
this.posX = parseInt((this.posX) - 1);
this.newPosY = this.posY;
this.newPosX = this.posX;
console.log("new position: " + this.newPosY + ", " + this.newPosX);
break;
}
}
};
}
Later, however, when an object is created and I try to move it on the board using the left-key, I get this console.message:
new position: undefined, NaN
Why?
For the complete code of this work in progress:
// * / constructor function pre-defining the player objects
function Player(name, posY, posX, currentTurn, numberMoves, weapon, previousWeapon) {
this.name = name;
this.posY = posY;
this.posX = posX;
this.currentTurn = currentTurn;
this.numberMoves = numberMoves;
this.weapon = weapon;
this.locatePosition = function() {
var l = 0;
var m = 0;
for (l = 0; l < mapArray.length; l++) {
for (m = 0; m < mapArray.length; m++) {
if ((mapArray[l][m]) === this.number) {
this.posY = [l];
this.posX = [m];
console.log("Player Position Y: " + this.posY + " + position X: " + this.posX);
}
}
}
}
//
this.movement = function() {
document.onkeydown = (e) => {
switch (e.which) {
case 37: // left - 1
this.numberMoves += 1;
console.log("player's number of moves made:" + this.numberMoves);
this.posX = parseInt((this.posX) - 1);
this.newPosY = this.posY;
this.newPosX = this.posX;
console.log("new position: " + this.newPosY + ", " + this.newPosX);
break;
}
}
};
}
// * creating two Player Objects
var player1 = new Player("Red Sonja");
var player2 = new Player("Robin");
// array that will be useful for a function that answers the question: "Whose turn is it?": determineInitialTurn
var turnArray = [player1, player2];
//* function that picks a random element of an array, a function we use in determineInitialTurn
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
function determineInitialTurn() {
shuffleArray(turnArray);
turnArray[0].currentTurn = true; // this player will have the first turn.
turnArray[1].currentTurn = false;
console.log("Is it " + (turnArray[0].name) + "'s turn? :" + turnArray[0].currentTurn);
};
//constructor for weapon objects
function Weapon(name, posY, posX, force) {
this.name = name;
this.posY = posY;
this.posX = posX;
this.force = force;
this.locatePositionWeapon = function() {
var l = 0;
var m = 0;
for (l = 0; l < mapArray.length; l++) {
for (m = 0; m < mapArray.length; m++) {
if ((mapArray[l][m]) === 6) {
this.posY = [l];
this.posX = [m];
console.log(this.position + " posY: " + this.posY + " + posX: " + this.posX);
}
}
}
}
};
// we create 4 weapon objects
var weapon1 = new Weapon("Tank", 10);
var weapon2 = new Weapon("Molotov", 8);
var weapon3 = new Weapon("Gun", 6);
var weapon4 = new Weapon("Bomb", 14);
// simple array where the weapons are listed. needed later in defining movement function.
var weapons = [weapon1, weapon2, weapon3, weapon4];
/*
bi-dimensional array with an initial distribution of elements.
Each number stands for an element in the game:
0 - empty
1 - rocktexture
*/
var mapArray = [ // bi-dimensional array with an initial distribution of elements (each number stands for an element in the game ()
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, weapon1, 0, player1, 0, 0, 0, 0, 0],
[0, 1, weapon2, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, weapon3, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, player2, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, weapon4, 0, 0, 0, 0, 0, 0],
];
// function that allows to shuffle the initial distribution of elements to create a random map.
function shuffleMap() {
function fisherYates(myArray) {
var i = myArray.length,
j, tempi, tempj;
if (i === 0) return false;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
tempi = myArray[i];
tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
}
}
mapArray.forEach(fisherYates);
};
// for each number in the array there is a div and we drap the map accordingly.
function drawMap() {
for (v = 0; v < mapArray.length; v++) {
for (w = 0; w < mapArray.length; w++) {
switch ((mapArray[v][w])) {
case weapon1:
$('#container').append('<div class="weapon1"></div>');
break;
case weapon2:
$('#container').append('<div class="weapon2"></div>');
break;
case weapon3:
$('#container').append('<div class="weapon3"></div>');
break;
case weapon4:
$('#container').append('<div class="weapon4"></div>');
break;
case player1:
$('#container').append('<div class="player1"></div>');
break;
case player2:
$('#container').append('<div class="player2"></div>');
break;
case 0:
$('#container').append('<div class="empty"></div>');
break;
case 1:
$('#container').append('<div class="rock"></div>');;
break;
}
}
}
};
// the program
$('document').ready(function() {
shuffleMap();
drawMap();
determineInitialTurn();
player1.locatePosition();
player2.locatePosition();
player1.movement();
player2.movement();
});
#container {
width: 40em;
height: 40em;
border: 1px solid black;
background-color: antiquewhite;
}
.empty {
height: 4em;
width: 4em;
position: relative;
float: left;
background-color: lawngreen;
}
.rock {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("http://img.hb.aicdn.com/91fd74edefe009c9058249832093409b505306dd65e1-h1FZVz_fw658");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
}
.weapon1 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("https://image.flaticon.com/icons/svg/544/544497.svg");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 90;
}
.weapon2 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url('https://image.flaticon.com/icons/svg/544/544497.svg');
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 90;
}
.weapon3 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("https://image.flaticon.com/icons/svg/544/544497.svg");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 90;
}
.weapon4 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("https://image.flaticon.com/icons/svg/544/544497.svg");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 90;
}
.player1 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("https://cdn2.iconfinder.com/data/icons/many-people-flat-icons/128/wonder-women-512.png");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 100;
}
.player2 {
height: 4em;
width: 4em;
position: relative;
float: left;
background-image: url("https://cdn2.iconfinder.com/data/icons/many-people-flat-icons/128/superman-128.png");
background-position: center;
background-repeat: no-repeat;
background-size: 4em;
z-index: 100;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Boarderino</title>
<meta name="description" content="a simple JS board-game">
<meta name="author" content="">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
<script src="js/main.js"></script>
<link rel="stylesheet" href="css/styles.css">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div id="container"></div>
</body>
</html>
The values of this.posX, this.posY, this.numberMoves, this.weapon are never given a value. True, they are assigned a value in the Person constructor, but you don't pass the arguments, so they will still be undefined.
You'll get something when doing:
var player1 = new Player("Red Sonja", 0, 0, false, 0);
// ^^^^^^^^^^^^^^^^
...etc.
There is a lot more that should be changed/fixed in your code, but it is too much to list. Just a few things:
A function locatePosition seems useless when you already have posX, posY. It is a waste of time to look for the item in the whole array, when you know where it is by its coordinates.
In a turn-based game, the number of moves made should not be stored for each player separately, but only once. It is common to store the "ply" number instead of the move number, i.e. the count of separate moves: 0 means player1 is to move, 1 means player2 is to move, 2 means player 1 is to move, ...etc.
Similarly, you should not store the fact whether it's a player's turn in its own properties. This should either be a separate variable, and only one, or even better be just derived from the ply-number (ply % 2).
In constructors you should not accept that many arguments. It is not practical, and often not necessary either. Instead, give default values for some of the properties.
Related
I have added the code snippet over here. I was trying to do some random exercise. Can someone look why my textbox is not editable. There is falling leaf animation over here. Along with I have added one textbox on top of it. But currently I am not able to add any text to the textbox.
I am just adding more text in order to overcome the error message that the post is mostly having code and not much explanation.
var LeafScene = function(el) {
this.viewport = el;
this.world = document.createElement('div');
this.leaves = [];
this.options = {
numLeaves: 20,
wind: {
magnitude: 1.2,
maxSpeed: 12,
duration: 300,
start: 0,
speed: 0
},
};
this.width = this.viewport.offsetWidth;
this.height = this.viewport.offsetHeight;
// animation helper
this.timer = 0;
this._resetLeaf = function(leaf) {
// place leaf towards the top left
leaf.x = this.width * 2 - Math.random()*this.width*1.75;
leaf.y = -10;
leaf.z = Math.random()*200;
if (leaf.x > this.width) {
leaf.x = this.width + 10;
leaf.y = Math.random()*this.height/2;
}
// at the start, the leaf can be anywhere
if (this.timer == 0) {
leaf.y = Math.random()*this.height;
}
// Choose axis of rotation.
// If axis is not X, chose a random static x-rotation for greater variability
leaf.rotation.speed = Math.random()*10;
var randomAxis = Math.random();
if (randomAxis > 0.5) {
leaf.rotation.axis = 'X';
} else if (randomAxis > 0.25) {
leaf.rotation.axis = 'Y';
leaf.rotation.x = Math.random()*180 + 90;
} else {
leaf.rotation.axis = 'Z';
leaf.rotation.x = Math.random()*360 - 180;
// looks weird if the rotation is too fast around this axis
leaf.rotation.speed = Math.random()*3;
}
// random speed
leaf.xSpeedVariation = Math.random() * 0.8 - 0.4;
leaf.ySpeed = Math.random() + 1.5;
return leaf;
}
this._updateLeaf = function(leaf) {
var leafWindSpeed = this.options.wind.speed(this.timer - this.options.wind.start, leaf.y);
var xSpeed = leafWindSpeed + leaf.xSpeedVariation;
leaf.x -= xSpeed;
leaf.y += leaf.ySpeed;
leaf.rotation.value += leaf.rotation.speed;
var t = 'translateX( ' + leaf.x + 'px ) translateY( ' + leaf.y + 'px ) translateZ( ' + leaf.z + 'px ) rotate' + leaf.rotation.axis + '( ' + leaf.rotation.value + 'deg )';
if (leaf.rotation.axis !== 'X') {
t += ' rotateX(' + leaf.rotation.x + 'deg)';
}
leaf.el.style.webkitTransform = t;
leaf.el.style.MozTransform = t;
leaf.el.style.oTransform = t;
leaf.el.style.transform = t;
// reset if out of view
if (leaf.x < -10 || leaf.y > this.height + 10) {
this._resetLeaf(leaf);
}
}
this._updateWind = function() {
// wind follows a sine curve: asin(b*time + c) + a
// where a = wind magnitude as a function of leaf position, b = wind.duration, c = offset
// wind duration should be related to wind magnitude, e.g. higher windspeed means longer gust duration
if (this.timer === 0 || this.timer > (this.options.wind.start + this.options.wind.duration)) {
this.options.wind.magnitude = Math.random() * this.options.wind.maxSpeed;
this.options.wind.duration = this.options.wind.magnitude * 50 + (Math.random() * 20 - 10);
this.options.wind.start = this.timer;
var screenHeight = this.height;
this.options.wind.speed = function(t, y) {
// should go from full wind speed at the top, to 1/2 speed at the bottom, using leaf Y
var a = this.magnitude/2 * (screenHeight - 2*y/3)/screenHeight;
return a * Math.sin(2*Math.PI/this.duration * t + (3 * Math.PI/2)) + a;
}
}
}
}
LeafScene.prototype.init = function() {
for (var i = 0; i < this.options.numLeaves; i++) {
var leaf = {
el: document.createElement('div'),
x: 0,
y: 0,
z: 0,
rotation: {
axis: 'X',
value: 0,
speed: 0,
x: 0
},
xSpeedVariation: 0,
ySpeed: 0,
path: {
type: 1,
start: 0,
},
image: 1
};
this._resetLeaf(leaf);
this.leaves.push(leaf);
this.world.appendChild(leaf.el);
}
this.world.className = 'leaf-scene';
this.viewport.appendChild(this.world);
// set perspective
this.world.style.webkitPerspective = "400px";
this.world.style.MozPerspective = "400px";
this.world.style.oPerspective = "400px";
this.world.style.perspective = "400px";
// reset window height/width on resize
var self = this;
window.onresize = function(event) {
self.width = self.viewport.offsetWidth;
self.height = self.viewport.offsetHeight;
};
}
LeafScene.prototype.render = function() {
this._updateWind();
for (var i = 0; i < this.leaves.length; i++) {
this._updateLeaf(this.leaves[i]);
}
this.timer++;
requestAnimationFrame(this.render.bind(this));
}
// start up leaf scene
var leafContainer = document.querySelector('.falling-leaves'),
leaves = new LeafScene(leafContainer);
leaves.init();
leaves.render();
body, html {
height: 100%;
}
form {
width: 600px;
margin: 0px auto;
padding: 15px;
}
input[type=text] {
display: block;
padding: 10px;
box-sizing: border-box;
font-size: x-large;
margin-top: 25%;
}
input[type=text] {
width: 100%;
margin-bottom: 15px;
}
.falling-leaves {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 100%;
max-width: 880px;
max-height: 880px; /* image is only 880x880 */
transform: translate(-50%, 0);
border: 20px solid #fff;
border-radius: 50px;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/125707/sidebar-bg.png) no-repeat center center;
background-size: cover;
overflow: hidden;
}
.leaf-scene {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 100%;
transform-style: preserve-3d;
}
.leaf-scene div {
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/125707/leaf.svg) no-repeat;
background-size: 100%;
transform-style: preserve-3d;
backface-visibility: visible;
}
<html>
<head>
<link rel="stylesheet" type="text/css" href="style/style.css">
</head>
<body>
<div class="falling-leaves">
<form id="frmContact">
<input type="text" id="txtName" name="txtName" placeholder="Text goes here">
</form>
</div>
<script src="script/script.js"></script>
</body>
</html>
I want to create a texture on a canvas. You can choose between 2 and 5 colours. For each colour you can specify the percentage. The distribution of the colours on the canvas should be even.
I hold the possible colors in an array and randomly select the index and thus assign the color. If i choose a big mixing ratio (i.e. 60% and 40%) it seems to work. But if the ratio gets smaller(i.e. 90%/10%), the result is not ok.
The problem is that towards the end of the line only the color with 90% appears.
For pixelmanipulation i use ImageData. My idea was to manipulate "row by row".
Please bear with me, I am JS beginner and do not speak English native
This is my Code:
generateTexture(colors) {
if (colors.length > 0) {
let myColors = colors.slice();
let canvas = document.getElementById('mycanvas');
const w = canvas.getBoundingClientRect().width * 4;
const h = canvas.getBoundingClientRect().height * 4;
let context = canvas.getContext('2d');
let i = 0;
let sum = w * h;
for (i = 0; i < myColors.length; i++) {
myColors[i].sum = (sum / 100) * myColors[i].percentage;
myColors[i].rowsum = myColors[i].sum / h;
}
let imageData = context.getImageData(0, 0, w, h);
let data = imageData.data;
let height = 0;
while (height <= h) {
for (i = 0; i < myColors.length; i++) {
myColors[i].sum = myColors[i].rowsum;
}
let start = 0;
start = height * h;
let end = start + w * 4;
let x = start;
while (x < end) {
let colIndex = 0;
if (colors.length > 1) {
colIndex = Math.floor(Math.random() * myColors.length);
}
if (myColors[colIndex].sum > 0) {
let val = myColors[colIndex].color.split(',');
let r = parseInt(val[0]);
let g = parseInt(val[1]);
let b = parseInt(val[2]);
data[x] = r;
data[x + 1] = g;
data[x + 2] = b;
data[x + 3] = 255;
myColors[colIndex].sum = myColors[colIndex].sum - 1;
x = x + 4;
}
}
height++;
}
context.putImageData(imageData, 0, 0);
canvas.style.webkitFilter = 'blur(.35px)';
}
},
},
There are several problems with your function.
Canvas resolution
canvas.getBoundingClientRect().width may or may not return the canvas resolution. What it returns is the size on the page. Use canvas.width and canvas.height to get the resolution of the canvas.
Wrong height value
You multiply height by 4. Thus the total number of pixels channels (RGBA) you are filling is w * 4 * h * 4 which 4 times too many. It should be w * h * 4
Randomness
The function generateTexture is not truly random as you are randomly picking from a small number of colors which does not represent the true distribution you are after with the percentages.
Say you have 90% blue and 10% red.
When you pick a color you randomly pick blue or red, 50/50 even odds, so for the first 20% of the image it will be 50% blue and 50% red. Then you run out of red (used up the 10%) and you have nothing to pick from but blue.
The result is bands of random distribution till the last which is just one color.
Example of poor distribution
Example of how your selection results in a very non random texture. (Banding)
function generateTexture(colors, ctx) {
var i;
const colSel = [...colors];
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
const pixels = data32.length;
for (const col of colSel) {
const rgb = col.color.split(',');
col.RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
col.pixels = Math.round(pixels * (col.percentage / 100));
}
i = 0;
while (i < pixels) {
const idx = Math.random() * colSel.length | 0;
const col = colSel[idx];
data32[i++] = col.RGBA32;
col.pixels --;
if (col.pixels <= 0) {
colSel.splice(idx, 1);
if (colSel.length === 1) {
const col = colSel[0];
while (i < pixels) { data32[i++] = col.RGBA32 }
}
}
}
ctx.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for another example run
<ul id="colList"> </ul>
</div>
Random items by percentage
You say this is a texture so, does it really matter if the number of actual pixel match the needed percentage.
If you create an array with 100 colors in it. For the 90% color add it to the array 90 times, for the 10% add it 10 times.
Then randomly pick from that array and you will get the distribution you want.
Example true random distribution
function generateTexture(colors, ctx) {
var len, i, colSel = [];
for (const col of colors) {
const rgb = col.color.split(',');
const RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
i = col.percentage;
while (i-- > 0) { colSel.push(RGBA32) }
}
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
i = len = data32.length;
while (i-- > 0) { data32[i] = colSel[Math.random() * 100 | 0] }
ctx.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for random texture
<ul id="colList"> </ul>
</div>
Shuffle pixels
If it is very important that the correct number of pixels for each color is in the image then you can use a random shuffle.
Add the colors 1 by 1 to the image to the exact counts needed.
Then randomly shuffle the pixels.
Example Random shuffle
function generateTexture(colors, ctx) {
var i, idx = 0, RGBA32;
const imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data32 = new Uint32Array(imgData.data.buffer);
const pixels = data32.length;
for (const col of colors) {
const rgb = col.color.split(',');
RGBA32 = 0xFF000000 + ((rgb[2] & 0xFF) << 16) + ((rgb[1] & 0xFF) << 8) + (rgb[0] & 0xFF);
i = Math.round((col.percentage / 100) * pixels);
while(i-- >= 0) { data32[idx++] = RGBA32 }
}
// fill any remaining pixels with last color
while(idx < pixels) { data32[idx++] = RGBA32 };
// shuffle pixels
i = 0;
while (i < pixels) {
const rIdx = Math.random() * (pixels-i) + i | 0;
const p = data32[i];
data32[i++] = data32[rIdx]
data32[rIdx] = p;
}
ctx.putImageData(imgData, 0, 0);
}
const colors = [];
const sum = 100;
var colCount = 0;
canvas.addEventListener("click", randomize);
function randomize(){
colors.length = colCount = 0;
colList.innerHTML = "";
while(colCount < 100) {
const col = randomColor();
colors.push(col);
$$(colList, $$($("li"),
$("span", {textContent: col.percentage + "%"}),
$("span", { className: "colBox", style: "background:rgb("+col.color+ ");"})
));
}
generateTexture(colors, canvas.getContext("2d"));
}
const randByte = () => Math.random() * 255 | 0;
function randomColor() {
const remaining = sum - colCount;
const percentage = remaining < 5 ? remaining : (Math.random()** 0.33)* remaining | 0;
colCount += percentage;
return {
color: [randByte(), randByte(), randByte()].join(","),
percentage,
}
}
const $$ = (el, ...sibs) => sibs.reduce((e,sib)=>(e.appendChild(sib), e), el);
const $ = (tag, p ={}) => Object.assign(document.createElement(tag),p);
randomize();
* {margin: 0px; font-family: arial}
canvas {
position: absolute;
top: opx;
left: 0px;
width: 100%;
height: 100%;
image-rendering: pixelated;
}
div {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 2px 4px;
border: 1px solid black;
}
#colList {
}
.colBox {
width: 64px;
height: 1.2em;
position: absolute;
right: 4px;
}
<canvas id="canvas" width="75" height="30"></canvas>
<div>Click for random texture
<ul id="colList"> </ul>
</div>
I've created the following code and I would like to have a trail following the dropped ball.
JSFiddle: https://jsfiddle.net/uj896hmq/72/
Code
var generateGame = function(){
var string = "";
var discAmount = 0;
for(var x = 0; x < 13; x++){
discAmount++;
string += "<div class='row'>";
for(var y = 1; y <= discAmount; y++){
string += "<div class='disc'></div>";
}
string += "</div>";
}
$('.board .wrapper').append(string);
var getPosition = $('.board').find('.disc').eq(0),
top = getPosition.position().top,
left = getPosition.position().left;
var $el = $('<div class="falling"></div>');
$('.board .wrapper').prepend($el);
$el.css({
top: top,
left: left
});
}
generateGame();
$(document).on('click', 'button', function(){
startGame();
});
var startGame = function(){
var $board = $('.board .wrapper'),
$el = $(".falling");
var currentRow = 0,
path = generatePath();
setInterval(function(){
var getPosition = $board.find('.row').eq(currentRow).find('.disc').eq(path[currentRow]),
top = getPosition.position().top,
left = getPosition.position().left;
$el.animate({
top: top,
left: left
}, 500);
//random between 1-2, 3-5, 4-8
currentRow++;
}, 500);
}
var generatePath = function(){
var path = [0];
for(var x = 1; x < 13; x++){
var previousPath = path[x - 1];
var randomPath = generateNext(previousPath, (previousPath + 1));
path.push(randomPath);
}
return path;
}
function generateNext(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
console.log(generatePath());
Point is I have no idea how I would achieve this with regular javascript and or jQuery, I thought of maybe placing a lot of divs at the position of the ball but that didn't seem proper.
Anyone has any ideas?
You can use SVG to dynamically add lines to the HTML without canvas. I've created a crude example that you can refer to. Basically it is just some calculations to position the lines.
var generateGame = function() {
var string = "";
var discAmount = 0;
for (var x = 0; x < 13; x++) {
discAmount++;
string += "<div class='row'>";
for (var y = 1; y <= discAmount; y++) {
string += "<div class='disc'></div>";
}
string += "</div>";
}
$('.board .wrapper').append(string);
var getPosition = $('.board').find('.disc').eq(0),
top = getPosition.position().top,
left = getPosition.position().left;
var $el = $('<div class="falling"></div>');
$('.board .wrapper').prepend($el);
$el.css({
top: top,
left: left
});
}
generateGame();
$(document).on('click', 'button', function() {
startGame();
});
var startGame = function() {
var $board = $('.board .wrapper'),
$el = $(".falling"),
$trail = $('.trail'),
$prevDisc = null;
var currentRow = 0,
path = generatePath();
$trail.empty();
setInterval(function() {
var getPosition = $board.find('.row').eq(currentRow).find('.disc').eq(path[currentRow])
if (getPosition.length > 0) {
var top = getPosition.position().top,
left = getPosition.position().left;
if ($prevDisc) {
var isLeft = left < $prevDisc.position().left;
drawPath($prevDisc.position().left, currentRow - 1, isLeft);
}
$prevDisc = getPosition;
$el.animate({
top: top,
left: left
}, 500);
//random between 1-2, 3-5, 4-8
currentRow++;
}
}, 500);
}
var generatePath = function() {
var path = [0];
for (var x = 1; x < 13; x++) {
var previousPath = path[x - 1];
var randomPath = generateNext(previousPath, (previousPath + 1));
path.push(randomPath);
}
return path;
}
function generateNext(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function drawPath(xPos, prevRow, isLeft) {
if (prevRow >= 0) {
var svgTopOffSet = 12;
var svgHeight = 22;
var svgWidth = 22;
var $trail = $('.trail');
var $newTrail = $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'));
$newTrail.attr({
x: xPos,
y: prevRow * svgHeight + svgTopOffSet
});
var $line = $(document.createElementNS('http://www.w3.org/2000/svg', 'line'));
$line.attr({
x1: svgWidth / 2,
y1: 0,
x2: isLeft ? 0 : svgWidth,
y2: svgWidth,
style: 'stroke:orange;stroke-width:3'
});
$newTrail.append($line);
$trail.append($newTrail);
}
}
body {
background: rgb(26, 30, 35);
}
.board {
width: 500px;
height: 300px;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
}
.row {
display: flex;
justify-content: center;
}
.row .disc {
height: 6px;
width: 6px;
border-radius: 50%;
background: gray;
margin: 8px;
}
.wrapper .falling {
position: absolute;
height: 11px;
width: 11px;
border-radius: 50%;
background: orange;
transform: translate(50%, 50%);
z-index: 1;
}
.wrapper {
position: relative;
}
.trail {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
line { stroke-dasharray: 25; stroke-dashoffset: 25; animation: offset 0.5s linear forwards; }
#keyframes offset {
to {
stroke-dashoffset: 0;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='board'>
<div class='wrapper'>
<svg class="trail">
</svg>
</div>
<button>
test game
</button>
</div>
I'm trying to convert this Popmotion example to GreenSock.
https://codepen.io/popmotion/pen/xVeWmm
var SELECTOR = '.box';
var velocityRange = [-1000, 1000];
var maxRotate = 30;
var smoothing = 100;
var box = ui.select(SELECTOR, {
values: {
x: 0,
y: 0,
rotateY: {
watch: function (actor) {
return actor.values.x.velocity;
},
mapFrom: velocityRange,
mapTo: [-maxRotate, maxRotate],
smooth: smoothing
},
rotateX: {
watch: function (actor) {
return actor.values.y.velocity;
},
mapFrom: velocityRange,
mapTo: [maxRotate, -maxRotate],
smooth: smoothing
}
}
});
var track2D = new ui.Track({
values: {
x: {},
y: {}
}
});
var springBack = new ui.Simulate({
simulate: 'spring',
spring: 500,
friction: 0.3,
values: {
x: 0,
y: 0
}
});
$('body').on('touchstart mousedown', SELECTOR, function (e) {
e.preventDefault();
box.start(track2D, e);
});
$('body').on('touchend mouseup', function () {
box.start(springBack);
});
Being a total noob at GreenSock, is this easy to do? Does GreenSock have actors and simulators?
I have never used GreenSock to make continuous dynamic animations (maybe it is possible, I am not an expert in GreenSock). I prefer to left this library to make a specific animation. In the next example, I've tried to replicate the same Popmotion effect you have posted using my own calculations and I just use the animation library to return the box to its original place. I think that it can help you in your purpose:
I've removed the vendor prefixes to make the code easier to read, but the CodePen example has the prefixes.
HTML Code:
<div id="container">
<div class="box"></div>
</div>
CSS Code
html {
height: 100%;
}
body {
background: #e25875;
height: 100%;
}
#container {
height: 100%;
perspective: 700;
perspective-origin: 50% 50%;
position: relative;
transform-style: preserve-3d;
width: 100%;
}
.box {
background: white;
border-radius: 4px;
height: 150px;
left: 50%;
margin-left: -75px;
margin-top: -75px;
position: absolute;
cursor: pointer;
top: 50%;
will-change: transform;
width: 150px;
}
JavaScript Code:
//---Variables
var doc = document,
box = doc.querySelector(".box"),
startX = 0,
startY = 0,
posX = 0,
posY = 0,
speedX = 0,
speedY = 0,
obj = {x: 0, y: 0, speedX: 0, speedY: 0};
//---Main Events
box.addEventListener("mousedown", startMove);
doc.addEventListener("mouseup", stopMove);
//---Start the movement
function startMove (evt) {
startX = evt.pageX;
startY = evt.pageY;
//---Add the mouse move events
doc.addEventListener("mousemove", updatePosition);
}
//---Update variables
function updatePosition (evt) {
speedX = (evt.pageX - posX) * 5;
speedY = (evt.pageY - posY) * 5;
if (speedX < -45) { speedX = -45 }
if (speedX > 45) { speedX = 45 }
if (speedY < -45) { speedY = -45 }
if (speedY > 45) { speedY = 45 }
posX = evt.pageX;
posY = evt.pageY;
obj.x += (posX - startX - obj.x) * .15;
obj.y += (posY - startY - obj.y) * .15;
obj.speedX += (speedX - obj.speedX) * .15;
obj.speedY += (speedY - obj.speedY) * .15;
updateTransform();
}
//---Stop movement, returns the box to its place
function stopMove () {
TweenLite.to(obj, 0.75, {
ease: Elastic.easeOut.config(1, 0.3),
x: 0,
y: 0,
speedX: 0,
speedY: 0,
onUpdate: updateTransform
});
doc.removeEventListener("mousemove", updatePosition);
}
//---Update the box transformations
function updateTransform () {
var transformStr = "translate(" + obj.x + "px, " + obj.y + "px) rotateX(" + (-obj.speedY) + "deg) rotateY(" + obj.speedX + "deg)";
box.style.transform = transformStr;
}
Here you have a CodePen with a working example.
EDIT: I've updated the CodePen to work with Touch Events.
CodePen
In the following snippet, you'll notice that an overlay asks you to 'Click Anywhere', and will add circles to the canvas on mousedown and mouseup events. Although the overlay text disappears on mousedown, If you click anywhere on the overlay element the canvas doesn't get the mousedown event to draw the start circle.
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.addEventListener('mousedown', addStartNode);
canvas.addEventListener('mouseup', addEndNode);
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
function addStartNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function addEndNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 10%;
left: 1em;
right: 1em;
font-size: 4em;
text-align: center;
z-index: 10;
background: rgba(255, 255, 255, 0.7);
}
#target {
position: relative;
width: 100%;
height: 100%;
background: aqua;
}
.node {
position: absolute;
background: blue;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
}
<div id="overlay">
Click Anywhere
</div>
<canvas id="target"></canvas>
I thought of just moving the event handler onto a container element or on the body element, which would catch the event as it propagates, but sometimes that's just not possible, such as when you're using an HTML5 game engine to process interactions.
Does anyone have any clever solutions for this?
EDIT:
What I really had in mind was a situation where translating the mouse position to the canvas was much more complicated, such as when a game engine or other canvas library is being used. In the following snippet I use PIXI.js. Note how you can drag the top left and middle bottom nodes, but not the node on the right which is covered by the text.
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
var nodeRadius = 50;
function Node(x, y) {
this.g = new PIXI.Graphics();
this.position.x = x;
this.position.y = y;
this.g.node = this;
this.drawCircle();
this.g.pivot = new PIXI.Point(nodeRadius / 2, nodeRadius / 2);
this.g.interactive = true;
this.defaultPos = new PIXI.Point(this.position.x, this.position.y);
this.g.on('mousedown', selectNode);
this.g.on('mouseup', connectNode);
this.g.on('mousemove', moveNode);
}
Node.prototype = {
reset: function() {
this.position.x = this.defaultPos.x;
this.position.y = this.defaultPos.y;
},
drawCircle: function() {
this.g.clear();
this.g.beginFill(0x3333FF, 1);
this.g.drawCircle(0, 0, nodeRadius);
this.g.endFill();
},
connectTo: function(node) {
this.drawCircle();
this.g.moveTo(0, 0);
this.g.lineStyle(5, 0xDDEEFF);
this.g.lineTo(node.position.x - this.position.x, node.position.y - this.position.y);
},
get position() {
return this.g.position;
},
set position(p) {
this.g.position = p;
}
};
var renderer = PIXI.autoDetectRenderer(600, 400),
container = new PIXI.Container(),
selectedNode = null;
container.position.x = -150;
container.position.y = 200;
container.scale.x = 0.3;
container.scale.y = 0.3;
var nodes = [
new Node(700, -500),
new Node(2200, 50),
new Node(1500, 450)
];
for (var i = 0; i < nodes.length; i++) {
container.addChild(nodes[i].g);
}
document.getElementById('content').appendChild(renderer.view);
requestAnimationFrame(animate);
function animate() {
renderer.render(container);
requestAnimationFrame(animate);
}
function selectNode() {
this.node.drawCircle();
selectedNode = this.node;
}
function connectNode() {
if (selectedNode) {
selectedNode.reset();
selectedNode = null;
}
}
function moveNode() {
if (selectedNode) {
var mousePos = renderer.plugins.interaction.mouse.getLocalPosition(container);
selectedNode.position.x = mousePos.x;
selectedNode.position.y = mousePos.y;
checkCollision();
}
}
function checkCollision() {
if (selectedNode) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] !== selectedNode && dist(selectedNode.position, nodes[i].position) < nodeRadius * 2) {
selectedNode.reset();
selectedNode.connectTo(nodes[i]);
selectedNode = null;
break;
}
}
}
}
function dist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 100px;
left: 10px;
width: 580px;
font-size: 4em;
text-align: center;
z-index: 100;
background: rgba(255, 255, 255, 0.5);
}
#content {
display: inline-block;
position: relative;
margin: 1em;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.js"></script>
</head>
<body>
<div id="content">
<div id="overlay">
Drag one node to another
</div>
<script src="script.js"></script>
</div>
</body>
</html>
I purposefully moved and scaled the container, because that happens often in games. The canvas itself may be offset from the page and may not even share the same parent as the overlay, which further complicates translating a mouse position to game coordinates.
This is why I was thinking it would be nice to somehow remove the overlay element before the mousedown event fires, because then we wouldn't have to worry about it.
Here is a fiddle.
Adding an eventlistener to the overlay as well should fix your problem:
document.getElementById("overlay").addEventListener('mousedown', addStartNode);
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.addEventListener('mousedown', addStartNode);
canvas.addEventListener('mouseup', addEndNode);
document.getElementById("overlay").addEventListener('mousedown', addStartNode);
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
function addStartNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function addEndNode(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 10%;
left: 1em;
right: 1em;
font-size: 4em;
text-align: center;
z-index: 10;
background: rgba(255, 255, 255, 0.7);
}
#target {
position: relative;
width: 100%;
height: 100%;
background: aqua;
}
.node {
position: absolute;
background: blue;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
}
<div id="overlay">
Click Anywhere
</div>
<canvas id="target"></canvas>
Events bubble up in the hierarchy of the dom. Since your 'hideOverlay' is an event on the body, the top most element, it won't bubble up to canvas. It would have to bubble down, which it doesn't. Since the overlay is positioned absolute, it's not inside the canvas, so a click on it won't bubble up to the canvas.
In short, the simple solution is to just have one event handler on a mousedown, eg body.mousedown which means you want to do the same thing where-ever the event takes place on the screen, namely hiding the overlay and drawing a circle on the canvas, so don't complicate things by having two event handlers. In body.mousedown, hide the overlay, and draw a circle.
Btw your code would also work as expected if you just call addStartNode from hideOverlay, but why would you?
You may simply use a unique event detection for each mouse event.
So first dropping the canvas.addEventListener()s, then gathering show/hide overlay and draw blue/green circle, like this:
var canvas = document.getElementById('target'),
context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.addEventListener('mousedown', mouseDown);
document.body.addEventListener('mouseup', mouseUp);
function mouseDown(evt) {
document.getElementById('overlay').style.display = 'none';
drawCircle(evt.clientX, evt.clientY, 10, 'green');
}
function mouseUp(evt) {
drawCircle(evt.clientX, evt.clientY, 10, 'blue');
document.getElementById('overlay').style.display = 'block';
}
function drawCircle(x, y, r, c) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, false);
context.fillStyle = c;
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}
nus' comment about using dispatchEvent does the trick.
document.getElementById('overlay').addEventListener('mousedown', function(evt) {
setTimeout(function() {
renderer.view.dispatchEvent(evt);
},0);
});
document.body.addEventListener('mousedown', hideOverlay);
document.body.addEventListener('mouseup', showOverlay);
function hideOverlay() {
document.getElementById('overlay').style.display = 'none';
}
function showOverlay() {
document.getElementById('overlay').style.display = 'block';
}
var nodeRadius = 50;
function Node(x, y) {
this.g = new PIXI.Graphics();
this.position.x = x;
this.position.y = y;
this.g.node = this;
this.drawCircle();
this.g.pivot = new PIXI.Point(nodeRadius / 2, nodeRadius / 2);
this.g.interactive = true;
this.defaultPos = new PIXI.Point(this.position.x, this.position.y);
this.g.on('mousedown', selectNode);
this.g.on('mouseup', connectNode);
this.g.on('mousemove', moveNode);
}
Node.prototype = {
reset: function() {
this.position.x = this.defaultPos.x;
this.position.y = this.defaultPos.y;
},
drawCircle: function() {
this.g.clear();
this.g.beginFill(0x3333FF, 1);
this.g.drawCircle(0, 0, nodeRadius);
this.g.endFill();
},
connectTo: function(node) {
this.drawCircle();
this.g.moveTo(0, 0);
this.g.lineStyle(5, 0xDDEEFF);
this.g.lineTo(node.position.x - this.position.x, node.position.y - this.position.y);
},
get position() {
return this.g.position;
},
set position(p) {
this.g.position = p;
}
};
var renderer = PIXI.autoDetectRenderer(600, 400),
container = new PIXI.Container(),
selectedNode = null;
document.getElementById('overlay').addEventListener('mousedown', function(evt) {
setTimeout(function() {
renderer.view.dispatchEvent(evt);
}, 0);
});
container.position.x = -150;
container.position.y = 200;
container.scale.x = 0.3;
container.scale.y = 0.3;
var nodes = [
new Node(700, -500),
new Node(2200, 50),
new Node(1500, 450)
];
for (var i = 0; i < nodes.length; i++) {
container.addChild(nodes[i].g);
}
document.getElementById('content').appendChild(renderer.view);
requestAnimationFrame(animate);
function animate() {
renderer.render(container);
requestAnimationFrame(animate);
}
function selectNode() {
this.node.drawCircle();
selectedNode = this.node;
}
function connectNode() {
if (selectedNode) {
selectedNode.reset();
selectedNode = null;
}
}
function moveNode() {
if (selectedNode) {
var mousePos = renderer.plugins.interaction.mouse.getLocalPosition(container);
selectedNode.position.x = mousePos.x;
selectedNode.position.y = mousePos.y;
checkCollision();
}
}
function checkCollision() {
if (selectedNode) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] !== selectedNode && dist(selectedNode.position, nodes[i].position) < nodeRadius * 2) {
selectedNode.reset();
selectedNode.connectTo(nodes[i]);
selectedNode = null;
break;
}
}
}
}
function dist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#overlay {
position: absolute;
top: 100px;
left: 10px;
width: 580px;
font-size: 4em;
text-align: center;
z-index: 100;
background: rgba(255, 255, 255, 0.5);
}
#content {
display: inline-block;
position: relative;
margin: 1em;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.8/pixi.js"></script>
</head>
<body>
<div id="content">
<div id="overlay">
Drag one node to another
</div>
<script src="script.js"></script>
</div>
</body>
</html>
Note that it needs to be done in a timeout or you get an InvalidStateError since "the event is already being dispatched".