Related
I'm trying to create a zoomable canvas with rectangles arranged in a grid using pixi.js. Everything works smoothly except that the grid creates heavy moire effects. My knowledge about pixijs and webgl is only very superficial but I'm suspecting that something with the antialiasing is not working as I expect it to. I'm drawing the rectangles using a 2048x2048px texture I create beforehand in separate canvas. I make it that big so I do this so I can zoom in all the way while still having a sharp rectangle. I also tried using app.renderer.generateTexture(graphics) but got a similar result.
The black rectangles are drawn using pixi.js and the red ones are drawn using SVG as a reference. There is still moire occurring in the SVG as well but it is much less. Any ideas how I can get closer to what the SVG version looks like? You can find a a working version here.
Here's the relevant code I use to setup the pixi.js application:
// PIXI SETUP
const app = new Application({
view: canvasRef,
width,
height,
transparent: true,
antialias: false,
autoDensity: true,
resolution: devicePixelRatio,
resizeTo: window
});
const particleContainer = new ParticleContainer(2500, {
scale: true,
position: true,
rotation: true,
uvs: true,
alpha: true
});
app.stage.addChild(particleContainer);
// TEXTURE
const size = 2048;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, size, size);
ctx.fill();
const texture = PIXI.Texture.from(canvas);
// RECTANGLE GRID
const size = 10;
for(let i=0; i<2500; i++) {
const particle = Sprite.from(texture);
particle.x = i % 50 * size * 1.5;
particle.y = Math.floor(i / 50) * size * 1.5;
particle.anchor.set(0);
particle.width = size;
particle.height = size;
parent.addChild(particle);
}
Don't render sub pixel detail.
The best way to maintain a grid while avoiding artifacts is to not render grid steps below the resolution of the canvas. Eg if you have zoomed out by 100 then do not draw grids less than 100 pixels.
As this can result in grid steps popping in and out you can fade grids in and out depending on the zoom level.
The example shows one way to do this. It still has some artifacts, these are hard to avoid, but this eliminates the horrid moire patterns you get when you render all the detail at every zoom level.
The grid is defined as 2D repeating patterns to reduce rendering overhead.
Also I find grid lines more problematic than grid squares (Demo has both)
This is very basic and can be adapted to any type of grid layout.
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const size = 138;
const grids = createPatterns(size, 4, "#222", "#EEE", "#69B", "#246");
var zoom = 1;
var zoomTarget = 16;
var zoomC = 0;
var gridType = 0;
var origin = {x: ctx.canvas.width / 2, y: ctx.canvas.height / 2};
const scales = [0,0,0];
function createPatterns(size, lineWidth, color1, color2, color3, color4){
function grid(col1, col2) {
ctx.fillStyle = col1;
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = col2;
ctx.fillRect(0, 0, size, lineWidth);
ctx.fillRect(0, 0, lineWidth, size);
}
function grid2(col1, col2) {
ctx.fillStyle = col1;
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = col2;
ctx.fillRect(0, 0, size / 2, size / 2);
ctx.fillRect( size / 2, size / 2, size / 2, size / 2);
}
const patterns = [];
const ctx = Object.assign(document.createElement("canvas"), {width: size, height: size}).getContext("2d");
grid(color1, color2)
patterns[0] = ctx.createPattern(ctx.canvas, "repeat");
grid2(color3, color4)
patterns[1] = ctx.createPattern(ctx.canvas, "repeat");
return patterns;
}
function drawGrid(ctx, grid, zoom, origin, smooth = true) {
function zoomAlpha(logScale) {
const t = logScale % 3;
return t < 1 ? t % 1 : t > 2 ? 1 - (t - 2) % 1 : 1;
}
function fillScale(scale) {
ctx.setTransform(scale / 8, 0, 0, scale / 8, origin.x, origin.y);
ctx.globalAlpha = zoomAlpha(Math.log2(scale));
ctx.fill();
}
ctx.fillStyle = grid;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.imageSmoothingEnabled = smooth;
ctx.beginPath();
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.globalAlpha = 1;
const l2 = Math.log2(zoom);
scales[0] = 2 ** ((l2 + 122) % 3); // zoom limit 1 / (2 ** 122) (well beyond number precision)
scales[1] = 2 ** ((l2 + 123) % 3);
scales[2] = 2 ** ((l2 + 124) % 3);
scales.sort((a,b) => a - b);
fillScale(scales[0]);
fillScale(scales[1]);
fillScale(scales[2]);
ctx.globalAlpha = 1;
}
function mainLoop() {
if (innerWidth !== ctx.canvas.width || innerHeight !== ctx.canvas.height) {
origin.x = (ctx.canvas.width = innerWidth) / 2;
origin.y = (ctx.canvas.height = innerHeight) / 2;
zoomTarget = 16;
zoom = 1;
}
zoomC += (zoomTarget - zoom) * 0.3;
zoomC *= 0.02;
zoom += zoomC;
if (gridType === 0) {
drawGrid(ctx, grids[0], zoom, origin);
} else {
drawGrid(ctx, grids[1], zoom, origin, false);
}
requestAnimationFrame(mainLoop);
}
zoomIn.addEventListener("click", () => zoomTarget *= 2);
zoomOut.addEventListener("click", () => zoomTarget *= 1/2);
toggle.addEventListener("click", () => gridType = (gridType + 1) % 2);
* { margin: 0px;}
canvas { position: absolute; top: 0px;left: 0px; }
.UI { position: absolute; top: 14px; left: 14px; }
<canvas id="canvas"></canvas>
<div class="UI">
<button id="zoomIn">Zoom In</button><button id="zoomOut">Zoom Out</button><button id="toggle">Toggle grid type</button>
</div>
Is it possible to disable smoothing of images within the canvas of Fabric.js when I export the canvas with toDataUrl()? I'm using version 3.6.2.
I have created the canvas with this code:
const canvas = new fabric.Canvas('canvas-1', {
imageSmoothingEnabled: false
});
imageSmoothingEnabled turns off the smoothing in the canvas. However, when I call toDataUrl() with an multiplier and I show the result in an img, the smoothing is still there and it was also multiplied.
const dataUrl = canvas.toDataURL({format: 'png', multiplier: 3});
$imgageElement.attr('src', dataUrl); //jQuery
What do I have to do so that I get exactly what is displayed in the canvas?
Here is a fiddle. And here is report, since I think it is a bug.
It doesn't completely resolve your problem, but you can somehow make it less evident using a Resize filter:
var img = new fabric.Image(img, {
left: 10,
top: 10,
angle: 5,
zoom: 3,
objectCaching: false
});
img.filters.push(new fabric.Image.filters.Resize({
resizeType: 'sliceHack',
scaleX: 4,
scaleY: 4
}));
img.applyFilters();
img.scale(4);
canvas.add(img);
Here is a working fiddle.
One workaround seems to be overriding toCanvasElement with:
const canvas = new fabric.Canvas('canvas-1', {
imageSmoothingEnabled: false
});
canvas.toCanvasElement = function (multiplier, cropping) {
multiplier = multiplier || 1;
cropping = cropping || {};
var scaledWidth = (cropping.width || this.width) * multiplier,
scaledHeight = (cropping.height || this.height) * multiplier,
zoom = this.getZoom(),
originalWidth = this.width,
originalHeight = this.height,
newZoom = zoom * multiplier,
vp = this.viewportTransform,
translateX = (vp[4] - (cropping.left || 0)) * multiplier,
translateY = (vp[5] - (cropping.top || 0)) * multiplier,
originalInteractive = this.interactive,
newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
originalRetina = this.enableRetinaScaling,
canvasEl = fabric.util.createCanvasElement(),
originalContextTop = this.contextTop;
canvasEl.width = scaledWidth;
canvasEl.height = scaledHeight;
this.contextTop = null;
this.enableRetinaScaling = false;
this.interactive = false;
this.viewportTransform = newVp;
this.width = scaledWidth;
this.height = scaledHeight;
this.calcViewportBoundaries();
var ctx = canvasEl.getContext('2d'); // replaced
ctx.imageSmoothingEnabled = false; // this.renderCanvas(canvasEl.getContext('2d'), this._objects);
this.renderCanvas(ctx, this._objects); // with these 3 lines
this.viewportTransform = vp;
this.width = originalWidth;
this.height = originalHeight;
this.calcViewportBoundaries();
this.interactive = originalInteractive;
this.enableRetinaScaling = originalRetina;
this.contextTop = originalContextTop;
return canvasEl;
};
I have a web page that is using Chart.js. In this page, I am rendering three donut charts. In the middle of each chart, I want to show the percentage of the donut that is filled. Currently, I have the following code, which can be seen in this Fiddle.
function setupChart(chartId, progress) {
var canvas = document.getElementById(chartId);
var context = canvas.getContext('2d');
var remaining = 100 - progress;
var data = {
labels: [ 'Progress', '', ],
datasets: [{
data: [progress, remaining],
backgroundColor: [
'#8FF400',
'#FFFFFF'
],
borderColor: [
'#8FF400',
'#408A00'
],
hoverBackgroundColor: [
'#8FF400',
'#FFFFFF'
]
}]
};
var options = {
responsive: true,
maintainAspectRatio: false,
scaleShowVerticalLines: false,
cutoutPercentage: 80,
legend: {
display: false
},
animation: {
onComplete: function () {
var xCenter = (canvas.width / 2) - 20;
var yCenter = canvas.height / 2 - 40;
context.textAlign = 'center';
context.textBaseline = 'middle';
var progressLabel = data.datasets[0].data[0] + '%';
context.font = '20px Helvetica';
context.fillStyle = 'black';
context.fillText(progressLabel, xCenter, yCenter);
}
}
};
Chart.defaults.global.tooltips.enabled = false;
var chart = new Chart(context, {
type: 'doughnut',
data: data,
options: options
});
}
The chart "runs". But the problem is with the rendering of the label. The label is not vertically and horizontally centered within the middle of the donut. The position of the label changes based on the screen resolution too. You can see the label change position by resizing the frame that the charts are rendered in within the Fiddle.
My question is, how do I consistently position the percentage in the middle of the donut? My page is a responsive page so, having consistent positioning is important Yet, I'm not sure what else to do. I feel like the textAlign and textBaseline properties aren't working, or I'm misunderstanding their usage.
Thanks!
According to this answer, you could do it with a plugin in a further version. I don't know if you noticed, but if you increase the width and reduce it over and over again on your fiddle, the height of your canvas gets bigger and bigger (the chart goes further and further down).
On the version you are using, you could extend the doughnut controller like this:
http://jsfiddle.net/ivanchaer/cutkqkuz/1/
Chart.defaults.DoughnutTextInside = Chart.helpers.clone(Chart.defaults.doughnut);
Chart.controllers.DoughnutTextInside = Chart.controllers.doughnut.extend({
draw: function(ease) {
var ctx = this.chart.chart.ctx;
var data = this.getDataset();
var easingDecimal = ease || 1;
Chart.helpers.each(this.getDataset().metaData, function(arc, index) {
arc.transition(easingDecimal).draw();
var vm = arc._view;
var radius = (vm.outerRadius + vm.innerRadius) / 2;
var thickness = (vm.outerRadius - vm.innerRadius) / 2;
var angle = Math.PI - vm.endAngle - Math.PI / 2;
ctx.save();
ctx.fillStyle = vm.backgroundColor;
ctx.font = "20px Verdana";
ctx.textAlign = 'center';
ctx.textBaseline = "middle";
var text = data.data[0] + '%';
ctx.fillStyle = '#000';
ctx.fillText(text, $(ctx.canvas).width()/2, $(ctx.canvas).height()/2);
});
ctx.fillStyle = '#fff';
ctx.fill();
ctx.restore();
}
});
function setupChart(chartId, progress) {
var canvas = document.getElementById(chartId);
var context = canvas.getContext('2d');
var remaining = 100 - progress;
var deliveredData = {
labels: [
"Value"
],
datasets: [{
data: [progress, remaining],
backgroundColor: [
'#8FF400',
'#FFFFFF'
],
borderColor: [
'#8FF400',
'#408A00'
],
hoverBackgroundColor: [
'#8FF400',
'#FFFFFF'
]
}]
};
var deliveredOpt = {
cutoutPercentage: 88,
animation: false,
legend: {
display: false
},
tooltips: {
enabled: false
}
};
var chart = new Chart(canvas, {
type: 'DoughnutTextInside',
data: deliveredData,
options: deliveredOpt
});
}
setupChart('standChart', 33);
setupChart('moveChart', 66);
setupChart('exerciseChart', 99);
See this http://jsfiddle.net/uha5tseb/2/
It can be handled with getting width/height of each chart by this reference
onComplete: function (event) {
console.log(this.chart.height);
var xCenter = this.chart.width/2;
var yCenter = this.chart.height/2;
context.textAlign = 'center';
context.textBaseline = 'middle';
var progressLabel = data.datasets[0].data[0] + '%';
context.font = '20px Helvetica';
context.fillStyle = 'black';
context.fillText(progressLabel, xCenter, yCenter);
}
Hope this solves your problem!
You set your variables incorrectly when you get xCenter and yCenter. Currently they don't get the middle of the canvas.
You have:
var xCenter = (canvas.width / 2) - 20;
var yCenter = canvas.height / 2 - 40;
It should be:
var xCenter = (canvas.width / 2);
var yCenter = (canvas.height / 2);
Im not familiar with chart.js, but as far as I understood from the documentation, they provide generateLegend method for your purposes. It returns a data from legendCallback that you can display on the page outside of canvas.
Container of the legend text can be aligned relatively to the canvas's wrapper.
HTML:
<div class="chart" data-legend="">
<canvas id="standChart"></canvas>
</div>
JS:
var container = canvas.parentElement;
...
container.setAttribute('data-legend', chart.generateLegend());
Fiddle
One way to resolve this is by adding an absolutely positioned element on top of the canvas.
You can add a div element after each canvas element and then set its style to:
.precentage {
font-family: tahoma;
font-size: 28px;
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}
Here is a complete JSFiddle.
StackOverflow community, I'm fairly new to JavaScript programming and I'm having some issues with the HTML5/JavaScript Canvas Combo.
The idea is to, following the click of a button, to verify some lines and, then, show on the screen the image (pre-defined) with some text in specific coordinates, the text depends on the input value, as they are stored in an Array.
The thing is, I'm not being able to draw the text on the Image, only the Image appears. The code goes as follows:
Globally Defined:
...
canvas = document.getElementById("cvs"),
ctx = canvas.getContext('2d'),
...
The Draw() function:
function draw() {
if (img_is_loaded) {
img = document.getElementById("background-A");
var maxh = 600,
maxw = 900,
height = img.height,
width = img.width,
name_input = name[0],
note_input = note[0],
date_input = date[0],
sur_input = sur[0];
while (height > maxh || width > maxw) {
--height;
--width;
}
canvas.height = height;
canvas.width = width;
ctx.save();
ctx.clearRect(0, 0, width, height);
ctx.drawImage(img, 0, 0, width, height);
ctx.font = "54px Times Roman";
ctx.fillStyle = "#000000";
ctx.fillText = (name_input, 35, 320);
ctx.font = "21px Times Roman";
ctx.fillStyle = "#000000";
ctx.fillText = (note_input, 61, 432);
ctx.fillText = (date_input, 254, 501);
ctx.fillText = (sur_input, 254, 528);
}
} else {
setTimeout(draw, 100);
}
}
I'm getting the Image, but no text. I'm aware that the issue, normally, is that I need to draw them "at the same time", so I tried:
function draw() {
if (img_is_loaded) {
var imageObj = new Image();
imageObj.onload = function(){
var maxh = 600,
maxw = 900,
height = imageObj.height,
width = imageObj.width,
name_input = name[0],
note_input = note[0],
date_input = date[0],
sur_input = sur[0];
while (height > maxh || width > maxw) {
--height;
--width;
}
canvas.height = height;
canvas.width = width;
ctx.save();
ctx.clearRect(0, 0, width, height);
$('#spinner-generate').hide();
ctx.drawImage(imageObj, 0, 0, width, height);
ctx.font = "54px Times Roman";
ctx.fillStyle = "#000000";
ctx.fillText = (name_input, 35, 320);
ctx.font = "21px Times Roman";
ctx.fillStyle = "#000000";
ctx.fillText = (note_input, 61, 432);
ctx.fillText = (date_input, 254, 501);
ctx.fillText = (sur_input, 254, 528);
ctx.restore();
}
imageObj.src = "IMAGEPATH";
} else {
setTimeout(draw, 100);
}
}
But, in this case, the "imageObj.onload" function gets skipped by the code, that automatically jumps to "else".
I'm really confused at this point, since every source I consulted says that it is "simple" and uses the imageObj.onload example, but with the "window.onload" combination.
Is there something I'm missing regarding the Canvas' "Order of things"?
I appreciate and thank in advance for any response or input.
You're using fillText as a property while it should have been used as a method - simply change the lines to this format:
ctx.fillText(name_input, 35, 320);
I did not check the rest of the code.
I have the following web page, which displays a number of images and a 'score bar' on an HTML5 canvas:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
canvas{
border: 1px solid #9C9898;
background:#F5F5F5;
}
</style>
<div id="container"></div>
<script src="kinetic.js"></script>
<script src="drawdescriptionboxes.js"></script>
<script src="drawLevelOneElements.js"></script>
<script src="startGameDrawGameElementsDrawStartButton.js"></script>
<script>
/*Add the game elements' global variables */
var currentLevel = 1;
var totalLevels = 3;
var currentScore = 0;
var currentScorePositionX = 950;
var currentScorePositionY = 10;
/*Add code to draw images to random locations here */
var imageX = Math.floor(Math.random()*950);
var imageY = Math.floor(Math.random()*450);
var stage = new Kinetic.Stage({
container: "container",
width: 1000,
height: 500
});
var imagesLayer = new Kinetic.Layer();
var canvas = imagesLayer.getCanvas();
var context = canvas.getContext("2d");
console.log("Foo ");
/*Load the images from the HTML into the JavaScript */
function loadImages(sources, callback){
var imagesDir = "";
var images = {};
var loadedImages = 0;
var numImages = 0;
//console.log("length " + sources.length);
for (var src in sources){
numImages++;
}
//console.log("Num Images " + numImages);
var index=0;
console.log("length " + sources.length);
for (index=0;index < numImages ;index++){
console.log(index);
images[index] = new Image();
images[index].src = sources[index];
console.log("Adding " + sources[index]);
callback(images[index]);
console.log("images array length = " + images.length);
}
stage.add(imagesLayer); // should only be added once!!
}
/*Function to check whether the item being dragged is near its description box */
function isNearDescriptionBox(itemImage, descriptionBox){
var ii = itemImage;
var db = descriptionBox;
if(ii.attrs.x > db.x - 20 && ii.attrs.x < db.x + 20 && ii.attrs.y > db.y - 20 && ii.attrs.y < db.y +20){
return true;
}else{
return false;
}
}
/* This function draws the game elements */
function drawGameElements(){
/* Draw a line for the 'score bar'. */
context.moveTo(0, 25);
context.lineTo(1000, 25);
context.stroke();
/* Draw current level/ total levels on the left, and current score on the right. */
context.font = "11pt Calibri"; /* Text font & size */
context.strokeStyle = "black"; /* Font colour */
context.strokeText(currentLevel + "/" + totalLevels, 10, 15);
context.strokeText(currentScore, 750, 15);
}
function initStage(images){
var stage = new Kinetic.Stage({
container: "container",
width: 1000,
height: 500
});
var descriptionLayer = new Kinetic.Layer();
//var imagesLayer = new Kinetic.Layer();
var allImages = [];
var currentScore = 0;
var descriptionBoxes = {
assetsDescriptionBox: {
x: 70,
y: 400
},
liabilitiesDescriptionBox: {
x: 300,
y: 400
},
incomeDescriptionBox: {
x: 530,
y: 400
},
expenditureDescriptionBox: {
x: 760,
y: 400
},
};
/*Code to detect whether image has been dragged to correct description box */
for (var key in sources){
/*Anonymous function to induce scope */
(function(){
var privateKey = key;
var imageSource = sources[key];
/*Check if image has been dragged to the correct box, and add it to that box's
array and remove from canvas if it has */
canvasImage.on("dragend", function(){
var descriptionBox = descriptionBoxes[privateKey];
if(!canvasImage.inRightPlace && isNearDescriptionBox(itemImage, descriptionBox)){
context.remove(canvasImage);
/*Will need to add a line in here to add the image to the box's array */
}
})
})();
}
}
function drawImage(imageObj) {
//var layer = new Kinetic.Layer();
var canvasImage = new Kinetic.Image({
image: imageObj,
width: 50,
height: 50,
// puts the image in teh middle of the canvas
x: stage.getWidth() / 2 - 50 / 2,
y: stage.getHeight() / 2 - 50 / 2,
draggable: true
});
// add cursor styling
canvasImage.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
canvasImage.on('mouseout', function() {
document.body.style.cursor = 'default';
});
imagesLayer.add(canvasImage);
}
/*This code loads the images to the canvas when the browser window loads */
window.onload = function(){
var sources = {};
sources[0] = document.getElementById("building").src,
sources[1] = document.getElementById("chair").src,
sources[2] = document.getElementById("drink").src,
sources[3] = document.getElementById("food").src,
sources[4] = document.getElementById("fridge").src,
sources[5] = document.getElementById("land").src,
sources[6] = document.getElementById("money").src,
sources[7] = document.getElementById("oven").src,
sources[8] = document.getElementById("table").src,
sources[9] = document.getElementById("van").src,
sources[10] = document.getElementById("burger").src,
sources[11] = document.getElementById("chips").src,
sources[12] = document.getElementById("drink").src,
sources[13] = document.getElementById("franchiseFee").src,
sources[14] = document.getElementById("wages").src,
sources[15] = document.getElementById("admin").src,
sources[16] = document.getElementById("cleaners").src,
sources[17] = document.getElementById("electricity").src,
sources[18] = document.getElementById("insurance").src,
sources[19] = document.getElementById("manager").src,
sources[20] = document.getElementById("rates").src,
sources[21] = document.getElementById("training").src,
sources[22] = document.getElementById("water").src,
sources[23] = document.getElementById("burger").src,
sources[24] = document.getElementById("chips").src,
sources[25] = document.getElementById("drink").src,
sources[26] = document.getElementById("creditors").src,
sources[27] = document.getElementById("electricity").src,
sources[28] = document.getElementById("food").src,
sources[29] = document.getElementById("hirePurchase").src,
sources[30] = document.getElementById("loan").src,
sources[31] = document.getElementById("overdraft").src,
sources[32] = document.getElementById("payeTax").src,
sources[33] = document.getElementById("tax").src
loadImages(sources, drawImage);
drawGameElements();
drawDescriptionBoxes();
};
</script>
The window.onload() function at the end is getting a load of images from a hidden section in the body of the HTML.
I have three JavaScript files that are being called in the HTML:
drawdescriptionboxes.js
function drawDescriptionBoxes(){
var assetsDescriptionBox = new Image();
var liabilitiesDescriptionBox = new Image();
var incomeDescriptionBox = new Image();
var expenditureDescriptionBox = new Image();
assetsDescriptionBox.src = 'images/box.png';
liabilitiesDescriptionBox.src = 'images/box.png';
incomeDescriptionBox.src = 'images/box.png';
expenditureDescriptionBox.src = 'images/box.png';
context.drawImage(assetsDescriptionBox, 70, 400, 120, 70);
context.drawImage(liabilitiesDescriptionBox, 300, 400, 120, 70);
context.drawImage(incomeDescriptionBox, 530, 400, 120, 70);
context.drawImage(expenditureDescriptionBox, 760, 400, 120, 70);
context.strokeText("Assets", 100, 490);
context.strokeText("Liabilities", 325, 490);
context.strokeText("Income", 550, 490);
context.strokeText("Expenditure", 775, 490);
}
function dragImageToBox(){
}
drawleveloneelements.js:
/* This function draws the elements for level 1. */
function drawLevelOneElements(){
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Call the function to enable drag and drop */
canvasState(document.getElementById('gameCanvas'));
/*Create the four description areas, and place them near the bottom of the canvas */
/*Create boxes with rounded corners for the description areas */
CanvasRenderingContext2D.prototype.drawDescriptionArea = function(x, y, width, height, radius, stroke){
if(typeof stroke == "undefined" ){
stroke = true;
}
if(typeof radius === "undefined"){
radius = 5;
}
this.beginPath();
this.moveTo(x + radius, y);
this.lineTo(x + width - radius, y);
this.quadraticCurveTo(x + width, y, x + width, y + radius);
this.lineTo(x + width, y + height - radius);
this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
this.lineTo(x + radius, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - radius);
this.lineTo(x, y + radius);
this.quadraticCurveTo(x, y, x + radius, y);
this.closePath();
if(stroke){
context.stroke();
}
}
context.drawDescriptionArea(70, 400, 120, 70);
context.font = '25pt Calibri';
context.strokeText('Asset', 90, 440);
context.drawDescriptionArea(300, 400, 120, 70);
context.strokeText('Liability', 310, 440);
context.drawDescriptionArea(540, 400, 120, 70);
context.strokeText('Income', 550, 440);
context.drawDescriptionArea(750, 400, 180, 70);
context.strokeText('Expenditure', 760, 440);
/*Now draw the images to the canvas */
/*First, create variables for the x & y coordinates of the image that will be drawn.
the x & y coordinates should hold random numbers, so that the images will be
drawn in random locations on the canvas.*/
var imageX = Math.floor(Math.random()*100);
var imageY = Math.floor(Math.random()*100);
var imageWidth = 50;
var imageHeight = 50;
/*Create a 'table' of positions that the images will be drawn to */
var imagePositionsX = [20, 80, 140, 200, 260, 320, 380, 440, 500, 560];
var imagePositionsY = [20, 60, 100, 140, 180, 220, 260, 300, 340, 380];
/*Draw all images from assetsImageArray */
/*Use a while loop to loop through the array, get each item and draw it. */
var arrayIteration = 0;
console.log('All Images Array length: ' + allImagesArray.length); /*Display the length of the array in the console, to check it's holding the correct number of images. */
while(arrayIteration < allImagesArray.length){
//var randomPositionX = Math.floor(Math.random()*10);
//var randomPositionY = Math.floor(Math.random()*10);
context.drawImage(allImagesArray[arrayIteration], imageX, imageY, imageWidth, imageHeight); /*Declare variables for image height and width, so it can be accessed elsewhere */
//allImagesArray[arrayIteration].setDraggable = "true";
allImagesArray[arrayIteration].setAttribute('draggable', 'true');
console.log(arrayIteration); /*Display the current array position that's being drawn */
arrayIteration = arrayIteration+1;
/*Now try changing the values of imageX & imageY so that the next image is drawn to a
different location*/
//imageX = imagePositionsX[randomPositionX]; /* imageX+(Math.floor(Math.random()*100)); */
//imageY = imagePositionsY[randomPositionY]; /* imageY+(Math.floor(Math.random()*100)); */
imageX = Math.floor(Math.random()*950);
imageY = Math.floor(Math.random()*350);
}
}
and startgamedrawgameelementsdrawstartbutton.js:
/* Global variables */
var image = new Image();
/* This function starts the game, and calls all of the other functions required to play the game */
function startGame(){
preLoadImages();
// drawGameElements(); /*Remove this call from function startGame(), so that the score bar doesn't show while the start button is displayed */
drawStartButton();
/*Add event listener to the canvas, which will call drawLevelOneElements()
when the start button is clicked*/
myGameCanvas.addEventListener('click', function(e){
console.log('click: ' + e.pageX + '/' + e.pageY);
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
if((mouseX > 260.5 && mouseX < 410.5+179) && (mouseY > 120 && mouseY < 120+180)){ /* Changing the '60' coordinates to 206 works for some reason */
drawLevelOneElements();
init();
/*Disable the event listener for clicking start button, because it continues to listen for a click */
this.removeEventListener('click',arguments.callee,false);
} else {
console.log('no collision');
}
}, false);
}
/* This function draws the game elements */
function drawGameElements(){
/* Draw a line for the 'score bar'. */
context.moveTo(0, 25);
context.lineTo(1000, 25);
context.stroke();
/* Draw current level/ total levels on the left, and current score on the right. */
context.font = "11pt Calibri"; /* Text font & size */
context.strokeStyle = "black"; /* Font colour */
context.strokeText(currentLevel + "/" + totalLevels, 10, 15);
context.strokeText(currentScore, currentScorePositionX, currentScorePositionY);
}
/* This function draws a start button which the user can click to start the game */
function drawStartButton(){
image.onload = function(){
context.drawImage(image, 410.5, 120);
};
image.src = "startButton.png";
/* Now I need to add an event listener to the area of the canvas on
on which the button image is displayed, in order to 'listen' for
a click on the button */
var boundingBox = myGameCanvas.getBoundingClientRect();
//var mouseX = (mouse_event.clientX-boundingBox.left) * (myGameCanvas.width/boundingBox.width);
//var mouseY = (mouse_event.clientY-boundingBox.top) * (myGameCanvas.height/boundingBox.height);
boundingBox.onmousemove = function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
var pixels = context.getImageData(mouseX, mouseY, 1, 1);
}
}
When I view the page in a browser by right clicking the file, and selecting 'open with Firefox', all of the images are displayed, and I'm able to drag and drop them around the canvas. However, when viewing the page at the URL at which I've located it, http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/ as soon as you click one of the images displayed, everything disappears from the canvas.
I can't figure out why this is, given that all the files I have saved locally are exactly the same as those saved on the server.
Anyone have any ideas?
Have you looked at the console?
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/drawLevelOneElements.js 404 (Not Found) users.aber.ac.uk:18
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/startButton.png 404 (Not Found) users.aber.ac.uk:18
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/liabilities/hirePurchase.jpg 404 (Not Found) users.aber.ac.uk:18
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/liabilities/payeTax.jpg 404 (Not Found) users.aber.ac.uk:18
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/expenses/direct/franchiseFee.jpg 404 (Not Found) users.aber.ac.uk:18
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/liabilities/hirePurchase.jpg 404 (Not Found) users.aber.ac.uk:62
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/expenses/direct/franchiseFee.jpg 404 (Not Found) users.aber.ac.uk:62
GET http://users.aber.ac.uk/eef8/project/development/dragimagestoboxes/images/liabilities/payeTax.jpg 404 (Not Found) users.aber.ac.uk:62
Looks like you forgot to upload some files.