This problem is (probably) not caused by the width/height being in the CSS, because it isn't. (How to avoid HTML Canvas auto-stretching)
When trying to make rage inputs to demonstrate an issue I was having, I found that when using range inputs it acted stranger. It seems to be representing a larger number than what I'm passing in.
Please check my code out and tell me what's causing all this. http://codepen.io/Magnesium/pen/wMwwgx
var engine = function(canvas) { // Trying to make a game engine when the problems started
this.canvas = canvas.getContext("2d");
this.defaultColor = "#FF0000";
this.clearCanvas = function() { // Clears canvas
this.canvas.clearRect(0, 0, canvas.width, canvas.height);
};
this.square = function(x, y, size, color) { // Makes a square. I don't see any problems, but if the error is caused by me it would probably be here
if (color == undefined) color = this.defaultColor;
this.canvas.fillStyle = color;
this.canvas.fillRect(x, y, x + size, y + size);
};
}
var canvas = document.getElementById("canvas");
var rangeX = document.getElementById("x");
var rangeY = document.getElementById("y");
var size = document.getElementById("sz");
var outX = document.getElementById("ox");
var outY = document.getElementById("oy");
var outSize = document.getElementById("osz");
var out = document.getElementById("out");
var enjin = new engine(canvas); // New engine
var defalt = false;
var src = document.getElementById("src");
setInterval(function() { // Called ~30 times a second
enjin.clearCanvas();
defalt ? enjin.square(50, 50, 50) : enjin.square(rangeX.value, rangeY.value, size.value);
defalt ? out.innerHTML = "enjin.square(50, 50, 50);" : out.innerHTML = "enjin.square("+rangeX.value+", "+rangeY.value+", "+size.value+");";
defalt ? src.innerHTML = "Using in-code values" : src.innerHTML = "Using slider values";
outX.innerHTML = rangeX.value;
outY.innerHTML = rangeY.value;
outSize.innerHTML = size.value;
}, 30);
The values you're passing to enjin.square are strings. You need to parse them to int:
enjin.square(parseInt(rangeX.value), parseInt(rangeY.value), parseInt(size.value));
Here's a working example:
var engine = function(canvas) { // Trying to make a game engine when the problems started
this.canvas = canvas.getContext("2d");
this.defaultColor = "#FF0000";
this.clearCanvas = function() { // Clears canvas
this.canvas.clearRect(0, 0, canvas.width, canvas.height);
};
this.square = function(x, y, size, color) { // Makes a square. I don't see any problems, but if the error is caused by me it would probably be here
if (color == undefined) color = this.defaultColor;
this.canvas.fillStyle = color;
this.canvas.fillRect(x, y, x + size, y + size);
};
}
var canvas = document.getElementById("canvas");
var rangeX = document.getElementById("x");
var rangeY = document.getElementById("y");
var size = document.getElementById("sz");
var outX = document.getElementById("ox");
var outY = document.getElementById("oy");
var outSize = document.getElementById("osz");
var out = document.getElementById("out");
var enjin = new engine(canvas); // New engine
var defalt = false;
var src = document.getElementById("src");
setInterval(function() { // Called ~30 times a second
enjin.clearCanvas();
defalt ? enjin.square(50, 50, 50) : enjin.square(parseInt(rangeX.value), parseInt(rangeY.value), parseInt(size.value));
defalt ? out.innerHTML = "enjin.square(50, 50, 50);" : out.innerHTML = "enjin.square(" + rangeX.value + ", " + rangeY.value + ", " + size.value + ");";
defalt ? src.innerHTML = "Using in-code values" : src.innerHTML = "Using slider values";
outX.innerHTML = rangeX.value;
outY.innerHTML = rangeY.value;
outSize.innerHTML = size.value;
}, 30);
#canvas {
border: 1px solid black;
}
<canvas id='canvas' width='400' height='400'></canvas>
<br />X
<input type='range' id='x' /><span id='ox'></span>
<br />Y
<input type='range' id='y' /><span id='oy'></span>
<br />Size
<input type='range' id='sz' /><span id='osz'></span>
<br /><span id='out'></span>
<br />
<button onclick='defalt = !defalt;'>Default value toggle</button>
<span id='src'></span>
And a updated pen.
Related
I am currently working on a Javascript project and I am struggling with exporting the entire SVG image on the canvas. So far I've been only able to export the visible part of the canvas, with out the "hidden" parts.
How do I capture the full canvas content?
Is there a way to do it without messing around with the original canvas size?
I am using D3.js V3
Screenshot of my project
Here's my code:
var svgString;
window.onload = function(){
setTimeout(function() {
exportSVG = document.getElementById("canvas");
document.getElementById("canvas").style.fontFamily= "lato";
document.getElementById("canvas").style.width= exportSVG.getBBox().width * 1;
document.getElementById("canvas").style.height= exportSVG.getBBox().height * 1;
svgString = getSVGString(exportSVG);
console.log(exportSVG.getBBox().width + " / " + exportSVG.getBBox().height);
svgString2Image(svgString, exportSVG.getBBox().width, exportSVG.getBBox().height, 'png', save); // passes Blob and filesize String to the callback
console.log("svg export code loaded");
// console.log(svgString.getBBox().width); document.getElementById("canvas").getBBox().width
}, 5000);
};
function save(dataBlob, filesize) {
saveAs(dataBlob, 'D3 vis exported to PNG.png'); // FileSaver.js function
}
// Below are the functions that handle actual exporting:
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
function getSVGString(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles(svgNode);
appendCSS(cssStyleText, svgNode);
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles(parentElement) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.' + parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.' + parentElement.classList[c]);
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#' + id, selectorTextArr))
selectorTextArr.push('#' + id);
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.' + classes[c], selectorTextArr))
selectorTextArr.push('.' + classes[c]);
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if (!s.cssRules) continue;
} catch (e) {
if (e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str, arr) {
return arr.indexOf(str) === -1 ? false : true;
}
}
function appendCSS(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}
function svgString2Image(svgString, width, height, format, callback) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
canvas.toBlob(function(blob) {
var filesize = Math.round(blob.length / 1024) + ' KB';
if (callback) callback(blob, filesize);
});
};
image.src = imgsrc;
}
Simply change your <svg> viewBox attribute before you serialize it to a string so that it displays everything:
var svg = document.querySelector('svg');
var toExport = svg.cloneNode(true); // avoids having to reset everything afterward
// grab its inner content BoundingBox
var bb = svg.getBBox();
// update its viewBox so it displays all its inner content
toExport.setAttribute('viewBox', bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height);
toExport.setAttribute('width', bb.width);
toExport.setAttribute('height', bb.height);
var svgAsStr = new XMLSerializer().serializeToString(toExport);
var blob = new Blob([svgAsStr], {type: 'image/svg+xml'});
var img = new Image();
img.onload = drawToCanvas;
img.src = URL.createObjectURL(blob);
function drawToCanvas(evt) {
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
canvas.getContext('2d').drawImage(this, 0,0);
document.body.appendChild(canvas);
}
svg{border: 1px solid blue}
canvas{border: 1px solid green}
<svg width="50" height="50" viewBox="0 0 50 50">
<rect x="0" y="0" width="200" height="50" fill="#CCC"/>
</svg>
I am trying to resize my image with javascript.
First, I place the image on the canvas, so I can manipulate it.
I can make it smaller but the problem is I can't make it bigger with Scale transformation. To make it easy, I will insert my code here
<!DOCTYPE HTML>
<html>
<body>
<canvas id='canvas1'></canvas>
<hr>
<button id='read'>READ IMAGE</button>
<hr>
Scale <input type='range' value='1' min='0.25' max='2' step='0.25' id='scale'>
<br><button id='default2'>Default Scalling</button>
<hr/>
</body>
<script src='imagine.js'></script>
<script>
var canvas = document.getElementById('canvas1')
var obj = new pc(canvas)
obj.image2canvas("565043_553561101348179_1714194038_a.jpg")
var tes = new Array()
document.getElementById('read').addEventListener('click',function(){
tes = obj.image2read()
})
document.getElementById('scale').addEventListener('change',function(){
var scaleval = this.value
var xpos = 0
var ypos = 0
var xnow = 0
var ynow = 0
var objW = obj.width
var objH = obj.height
tesbackup = new Array()
for(var c=0; c<tes.length; c++){
temp = new Array()
for(var d=0; d<4; d++){
temp.push(255)
}
tesbackup.push(temp)
}
for(var i=0; i<tes.length; i++){
xpos = obj.i2x(i)
ypos = obj.i2y(i)
xnow = Math.round( (xpos)*(scaleval))
ynow = Math.round( (ypos)*(scaleval))
var idxnow = obj.xy2i(xnow,ynow)
tesbackup[idxnow][0] = tes[i][0]
tesbackup[idxnow][1] = tes[i][1]
tesbackup[idxnow][2] = tes[i][2]
}
obj.array2canvas(tesbackup)
})
</script>
And then for the script that used on that html file, is a js file named imagine.js.
imagine.js
function info(text){
console.info(text)
}
function pc(canvas){
this.canvas = canvas
this.context = this.canvas.getContext('2d')
this.width = 0
this.height = 0
this.imgsrc = ""
this.image2read = function(){
this.originalLakeImageData = this.context.getImageData(0,0, this.width, this.height)
this.resultArr = new Array()
this.tempArr = new Array()
this.tempCount = 0
for(var i=0; i<this.originalLakeImageData.data.length; i++){
this.tempCount++
this.tempArr.push(this.originalLakeImageData.data[i])
if(this.tempCount == 4){
this.resultArr.push(this.tempArr)
this.tempArr = []
this.tempCount = 0
}
}
info('image2read Success ('+this.imgsrc+') : '+this.width+'x'+this.height)
return this.resultArr
}
this.image2canvas = function(imgsrc){
var imageObj = new Image()
var parent = this
imageObj.onload = function() {
parent.canvas.width = imageObj.width
parent.canvas.height = imageObj.height
parent.context.drawImage(imageObj, 0, 0)
parent.width = imageObj.width
parent.height = imageObj.height
info('image2canvas Success ('+imgsrc+')')
}
imageObj.src = imgsrc
this.imgsrc = imgsrc
}
this.array2canvas = function(arr){
this.imageData = this.context.getImageData(0,0, this.width, this.height)
if(this.imageData.data.length != arr.length*4){
error("array2canvas Failed to Execute")
return false
}
for(var i = 0; i < arr.length; i++){
this.imageData.data[(i*4)] = arr[i][0]
this.imageData.data[(i*4)+1] = arr[i][1]
this.imageData.data[(i*4)+2] = arr[i][2]
this.imageData.data[(i*4)+3] = arr[i][3]
}
this.context.clearRect(0, 0, this.width, this.height)
this.context.putImageData(this.imageData, 0, 0)
info('Array2Canvas Success ('+this.imgsrc+')')
}
this.i2x = function(i){
return (i % this.width)
}
this.i2y = function(i){
return ((i - (i % this.width))/ this.width)
}
this.xy2i = function(x,y){
return (y * this.width) + (x)
}
}
I screenshot the output of the script, the image can be zoomed to a smaller image but can't be zoomed to a bigger image canvas
Original size of the image
Image size after I change the value to smaller value
But when I try to scroll it to a bigger value, the canvas get bigger but the images is disappeared. And I get the error "Cannot set property '0' of undefined".
Image size when I change the value to bigger value
Can someone point out what should I add or remove to get the image resize to bigger using this code??
Thanks in advance
I've been working on a simple matching puzzle game for a little while now. Currently I have been able to have a countdown time and the number of matching tiles displayed as a text and I'm trying to create one for the best time completed (basically how fast the person completed the puzzle). However whenever I try to create it the text never displays.I noticed while writing the code that if I where to place the variable "txt" followed by a "." the autocomplete box will appear with .text as an available option so I would get "txt.text". I do not however get that option when writing the bestTimeTxt variable which is what I am using to display the time. I'm not sure what I have done wrong, here is my code.
<!DOCTYPE html>
<html>
<head>
<title>Recipe: Drawing a square</title>
<script src="easel.js"></script>
<script type="text/javascript">
var canvas;
var stage;
var squareSide = 70;
var squareOutline = 5;
var max_rgb_color_value = 255;
var gray = Graphics.getRGB(20, 20, 20);
var placementArray = [];
var tileClicked;
var timeAllowable;
var totalMatchesPossible;
var matchesFound;
var txt;
var bestTime;
var bestTimeTxt;
var matchesFoundText;
var squares;
function init() {
var rows = 5;
var columns = 6;
var squarePadding = 10;
canvas = document.getElementById('myCanvas');
stage = new Stage(canvas);
var numberOfTiles = rows*columns;
matchesFound = 0;
timeAllowable = 5;
bestTime = 0
txt = new Text(timeAllowable, "30px Monospace", "#000");
txt.textBaseline = "top"; // draw text relative to the top of the em box.
txt.x = 500;
txt.y = 0;
bestTimeTxt = new Text(bestTime, "30px Monospace", "#000");
bestTimeTxt.textBaseLine = "top";
bestTimeTxt.x = 300;
bestTimeTxt.y = 0;
stage.addChild(txt);
stage.addChild(bestTimeTxt);
squares = [];
totalMatchesPossible = numberOfTiles/2;
Ticker.init();
Ticker.addListener(window);
Ticker.setPaused(false);
matchesFoundText = new Text("Pairs Found: "+matchesFound+"/"+totalMatchesPossible, "30px Monospace", "#000");
matchesFoundText.textBaseline = "top"; // draw text relative to the top of the em box.
matchesFoundText.x = 500;
matchesFoundText.y = 40;
stage.addChild(matchesFoundText);
setPlacementArray(numberOfTiles);
for(var i=0;i<numberOfTiles;i++){
var placement = getRandomPlacement(placementArray);
if (i % 2 === 0){
var color = randomColor();
}
var square = drawSquare(gray);
square.color = color;
square.x = (squareSide+squarePadding) * (placement % columns);
square.y = (squareSide+squarePadding) * Math.floor(placement / columns);
squares.push(square);
stage.addChild(square);
square.cache(0, 0, squareSide + squarePadding, squareSide + squarePadding);
square.onPress = handleOnPress;
stage.update();
};
}
function drawSquare(color) {
var shape = new Shape();
var graphics = shape.graphics;
graphics.setStrokeStyle(squareOutline);
graphics.beginStroke(gray);
graphics.beginFill(color);
graphics.rect(squareOutline, squareOutline, squareSide, squareSide);
return shape;
}
function randomColor(){
var color = Math.floor(Math.random()*255);
var color2 = Math.floor(Math.random()*255);
var color3 = Math.floor(Math.random()*255);
return Graphics.getRGB(color, color2, color3)
}
function setPlacementArray(numberOfTiles){
for(var i = 0;i< numberOfTiles;i++){
placementArray.push(i);
}
}
function getRandomPlacement(placementArray){
randomNumber = Math.floor(Math.random()*placementArray.length);
return placementArray.splice(randomNumber, 1)[0];
}
function handleOnPress(event){
var tile = event.target;
tile.graphics.beginFill(tile.color).rect(squareOutline, squareOutline, squareSide, squareSide);
if(!!tileClicked === false || tileClicked === tile){
tileClicked = tile;
tileClicked.updateCache("source-overlay");
}else{
if(tileClicked.color === tile.color && tileClicked !== tile){
tileClicked.visible = false;
tile.visible = false;
matchesFound++;
matchesFoundText.text = "Pairs Found: "+matchesFound+"/"+totalMatchesPossible;
if (matchesFound===totalMatchesPossible){
gameOver(true);
}
}else{
tileClicked.graphics.beginFill(gray).rect(squareOutline, squareOutline, squareSide, squareSide);
}
tileClicked.updateCache("source-overlay");
tile.updateCache("source-overlay");
tileClicked = tile;
}
stage.update();
}
function tick() {
secondsLeft = Math.floor((timeAllowable-Ticker.getTime()/1000));
txt.text = secondsLeft;
bestTimeTxt.text = "test";
if (secondsLeft <= 0){
gameOver(false);
}
stage.update();
}
function gameOver(win){
Ticker.setPaused(true);
for(var i=0;i<squares.length;i++){
squares[i].graphics.beginFill(squares[i].color).rect(5, 5, 70, 70);
squares[i].onPress = null;
if (win === false){
squares[i].uncache();
}
}
var replayParagraph = document.getElementById("replay");
replayParagraph.innerHTML = "<a href='#' onClick='history.go(0);'>Play Again?</a>";
if (win === true){
matchesFoundText.text = "You win!"
}else{
txt.text = secondsLeft + "... Game Over";
}
}
function replay(){
init();
}
</script>
</head>
<body onload="init()">
<header id="header">
<p id="replay"></p>
</header>
<canvas id="myCanvas" width="960" height="400"></canvas>
</body>
</html>
Apparently the issue I was having was that the x and y position of the text was causing it to appear behind everything else.
I'm currently working on a small tile matching game and have made it so that each time you complete the game the variable "bestTime" will store the amount of time you took to complete the session. The variable "bestTimeTxt" will then take the value and display it in text. After you have completed a session a link will appear allowing you to start again. I have put the new text
bestTimeTxt = new Text("Best Time: " + bestTime , "30px Monospace", "#000");
bestTimeTxt.textBaseLine = "top";
bestTimeTxt.x = 500;
bestTimeTxt.y = 100;
outside of the init() function so it shouldn't keep resetting I'm not sure what I am supposed to do as every combination i could think of isn't working.
here is my full code.
I'm also using easeljs for this game
<!DOCTYPE html>
<html>
<head>
<title>Recipe: Drawing a square</title>
<script src="easel.js"></script>
<script type="text/javascript">
var canvas;
var stage;
var squareSide = 70;
var squareOutline = 5;
var max_rgb_color_value = 255;
var gray = Graphics.getRGB(20, 20, 20);
var placementArray = [];
var tileClicked;
var timeAllowable;
var totalMatchesPossible;
var matchesFound;
var txt;
var bestTime = 0;
var bestTimeTxt;
var matchesFoundText;
var squares;
var startingTime;
bestTimeTxt = new Text("Best Time: " + bestTime , "30px Monospace", "#000");
bestTimeTxt.textBaseLine = "top";
bestTimeTxt.x = 500;
bestTimeTxt.y = 100;
function init() {
var rows = 5;
var columns = 6;
var squarePadding = 10;
canvas = document.getElementById('myCanvas');
stage = new Stage(canvas);
var numberOfTiles = rows*columns;
matchesFound = 0;
timeAllowable = 500;
startingTime = timeAllowable;
txt = new Text(timeAllowable, "30px Monospace", "#000");
txt.textBaseline = "top"; // draw text relative to the top of the em box.
txt.x = 500;
txt.y = 0;
stage.addChild(bestTimeTxt);
stage.addChild(txt);
squares = [];
totalMatchesPossible = numberOfTiles/2;
Ticker.init();
Ticker.addListener(window);
Ticker.setPaused(false);
matchesFoundText = new Text("Pairs Found: "+matchesFound+"/"+totalMatchesPossible, "30px Monospace", "#000");
matchesFoundText.textBaseline = "top"; // draw text relative to the top of the em box.
matchesFoundText.x = 500;
matchesFoundText.y = 40;
stage.addChild(matchesFoundText);
setPlacementArray(numberOfTiles);
for(var i=0;i<numberOfTiles;i++){
var placement = getRandomPlacement(placementArray);
if (i % 2 === 0){
var color = randomColor();
}
var square = drawSquare(gray);
square.color = color;
square.x = (squareSide+squarePadding) * (placement % columns);
square.y = (squareSide+squarePadding) * Math.floor(placement / columns);
squares.push(square);
stage.addChild(square);
square.cache(0, 0, squareSide + squarePadding, squareSide + squarePadding);
square.onPress = handleOnPress;
stage.update();
};
}
function drawSquare(color) {
var shape = new Shape();
var graphics = shape.graphics;
graphics.setStrokeStyle(squareOutline);
graphics.beginStroke(gray);
graphics.beginFill(color);
graphics.rect(squareOutline, squareOutline, squareSide, squareSide);
return shape;
}
function randomColor(){
var color = Math.floor(Math.random()*255);
var color2 = Math.floor(Math.random()*255);
var color3 = Math.floor(Math.random()*255);
return Graphics.getRGB(color, color2, color3)
}
function setPlacementArray(numberOfTiles){
for(var i = 0;i< numberOfTiles;i++){
placementArray.push(i);
}
}
function getRandomPlacement(placementArray){
randomNumber = Math.floor(Math.random()*placementArray.length);
return placementArray.splice(randomNumber, 1)[0];
}
function handleOnPress(event){
var tile = event.target;
tile.graphics.beginFill(tile.color).rect(squareOutline, squareOutline, squareSide, squareSide);
if(!!tileClicked === false || tileClicked === tile){
tileClicked = tile;
tileClicked.updateCache("source-overlay");
}else{
if(tileClicked.color === tile.color && tileClicked !== tile){
tileClicked.visible = false;
tile.visible = false;
matchesFound++;
matchesFoundText.text = "Pairs Found: "+matchesFound+"/"+totalMatchesPossible;
if (matchesFound===totalMatchesPossible){
gameOver(true);
}
}else{
tileClicked.graphics.beginFill(gray).rect(squareOutline, squareOutline, squareSide, squareSide);
}
tileClicked.updateCache("source-overlay");
tile.updateCache("source-overlay");
tileClicked = tile;
}
stage.update();
}
function tick() {
secondsLeft = Math.floor((timeAllowable-Ticker.getTime()/1000));
txt.text = secondsLeft;
;
if (secondsLeft <= 0){
gameOver(false);
}
stage.update();
}
function gameOver(win){
Ticker.setPaused(true);
for(var i=0;i<squares.length;i++){
squares[i].graphics.beginFill(squares[i].color).rect(5, 5, 70, 70);
squares[i].onPress = null;
if (win === false){
squares[i].uncache();
}
}
var replayParagraph = document.getElementById("replay");
replayParagraph.innerHTML = "<a href='#' onClick='history.go(0);'>Play Again?</a>";
if (win === true){
matchesFoundText.text = "You win!"
if((startingTime - secondsLeft) > bestTime)
{
bestTime = startingTime - secondsLeft;
bestTimeTxt.text = "Best Time: " + bestTime;
}
}
else
{
txt.text = secondsLeft + "... Game Over";
}
}
function replay(){
init();
}
</script>
</head>
<body onload="init()">
<header id="header">
<p id="replay"></p>
</header>
<canvas id="myCanvas" width="960" height="400"></canvas>
</body>
</html>
In your code, inside gameover method, I see you are using history.go(0) on play again link.
Technically, history.go(0) means to refresh the page and all your variables no matter the scope are set to the initial values.
If you want to retain the best score for the session and continue, use the replay method instead of history.
Updated Code :
replayParagraph.innerHTML = "<a href='#' onClick='replay();'>Play Again?</a>";
I have made a short quiz online following a tutorial, this is my first time using the canvas function. When I have typed my own questions, they appear as one line, and I am not sure how to break the lines up, in order for the question to display properly. Can anyone help?
Here is what I have so far:
<!DOCTYPE HTML>
<html>
<head>
<style>
body{
background-color: black;
}
#ccontainer{
width: 550px;
margin: 0 auto;
margin-top: 110px;
}
#myCanvas{
/*/ background: #FFF; /*/
}
</style>
<script>
window.onload = function(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var quizbg = new Image();
var Question = new String;
var Option1 = new String;
var Option2 = new String;
var Option3 = new String;
var mx=0;
var my=0;
var CorrectAnswer = 0;
var qnumber = 0;
var rightanswers=0;
var wronganswers=0;
var QuizFinished = false;
var lock = false;
var textpos1=25;
var textpos2=145;
var textpos3=230;
var textpos4=325;
var Questions = ["Which Manchester United Player won \n the 2008 Golden Boot with 31 Goals?","At which club did Bobby Charlton start his football career?","Which year did Wayne Rooney win the BBC Young Sports Personality of the year award?"];
var Options = [["Cristiano Ronaldo","Wayne Rooney","Ryan Giggs"],["Manchester United","Manchester City","Chelsea"],["2002","2003","2004"]];
quizbg.onload = function(){
context.drawImage(quizbg, 0, 0);
SetQuestions();
}
quizbg.src = "quizbg.png";
SetQuestions = function(){
Question=Questions[qnumber];
CorrectAnswer=1+Math.floor(Math.random()*3);
if(CorrectAnswer==1){Option1=Options[qnumber][0];Option2=Options[qnumber][1];Option3=Options[qnumber][2];}
if(CorrectAnswer==2){Option1=Options[qnumber][2];Option2=Options[qnumber][0];Option3=Options[qnumber][1];}
if(CorrectAnswer==3){Option1=Options[qnumber][1];Option2=Options[qnumber][2];Option3=Options[qnumber][0];}
context.textBaseline = "middle";
context.font = "16pt sans-serif,Arial";
context.fillText(Question,20,textpos1);
context.font = "14pt sans-serif,Arial";
context.fillText(Option1,20,textpos2);
context.fillText(Option2,20,textpos3);
context.fillText(Option3,20,textpos4);
}//SetQuestions
canvas.addEventListener('click',ProcessClick,false);
function ProcessClick(ev) {
my=ev.y-canvas.offsetTop;
if(ev.y == undefined){
my = ev.pageY - canvas.offsetTop;
}
if(lock){
ResetQ();
}//if lock
else{
if(my>110 && my<180){GetFeedback(1);}
if(my>200 && my<270){GetFeedback(2);}
if(my>290 && my<360){GetFeedback(3);}
}//!lock
}//process click
GetFeedback = function(a){
if(a==CorrectAnswer){
context.drawImage(quizbg, 0,400,75,70,480,110+(90*(a-1)),75,70);
rightanswers++;
//drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
}
else{
context.drawImage(quizbg, 75,400,75,70,480,110+(90*(a-1)),75,70);
wronganswers++;
}
lock=true;
context.font = "14pt sans-serif,Arial";
context.fillText("Click again to continue",20,380);
}//get feedback
ResetQ= function(){
lock=false;
context.clearRect(0,0,550,400);
qnumber++;
if(qnumber==Questions.length){EndQuiz();}
else{
context.drawImage(quizbg, 0, 0);
SetQuestions();}
}
EndQuiz=function(){
canvas.removeEventListener('click',ProcessClick,false);
context.drawImage(quizbg, 0,0,550,90,0,0,550,400);
context.font = "20pt sans-serif,Arial";
context.fillText("You have finished the quiz!",20,100);
context.font = "16pt sans-serif,Arial";
context.fillText("Correct answers: "+String(rightanswers),20,200);
context.fillText("Wrong answers: "+String(wronganswers),20,240);
}
};
</script>
</head>
<body>
<div id="ccontainer">
<canvas id="myCanvas" width="550" height="400"></canvas>
</div>
</body>
</html>
Thanks!
You can use context.measureText to get the width of given text.
Here's a function that wraps text using context.measureText to measure each word in a sentence and wrap to a new line when the current line exceeds a given width:
A Demo: http://jsfiddle.net/m1erickson/mQFDB/
function wrapText(context, text, x, y, maxWidth, fontSize, fontFace){
var words = text.split(' ');
var line = '';
var lineHeight=fontSize;
context.font = fontSize + "px " + fontFace;
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if(testWidth > maxWidth) {
context.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
}
else {
line = testLine;
}
}
context.fillText(line, x, y);
return(y);
}
You can draw your text on the canvas like this:
var lastY=wrapText(context,"Hello",20,40,100,14,"verdana");
The lastY variable holds the y-coordinate of the last line of wrapped text. Therefore you can begin new text at lastY plus some padding:
lastY=wrapText(context,"World",20,lastY+20,100,14,"verdana");
This pattern lets you make text-wrapped paragraphs down the canvas (or in your case questions and multiple choice answers down the canvas).