here's my HTML
<html>
<head>
<meta charset="UTF-8" />
<script src="script.js"></script>
</head>
....
and here's the javascript. everything was fine when i had the script inline, but when i move it outside of the html file it breaks. just a simple html canvas drawing but not sure the issue. ideas?
// Canvas 1
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
photo = document.getElementById("red");
function drawImage() {
context.drawImage(photo, 0, 0);
}
window.addEventListener("load", drawImage, false);
// Canvas 2
var canvas2 = document.getElementById("canvas2");
var context2 = canvas2.getContext("2d");
context2.fillStyle = "darkRed";
context2.fillRect(0, 2, 800, 500);
context2.moveTo(0, 0);
context2.lineTo(400, 300);
// Canvas 3
var canvas3 = document.getElementById("canvas3");
var context3 = canvas3.getContext("2d");
photo3 = document.getElementById("red2");
function drawImage() {
for (var x = 0; x < 6; x++) {
for (var y =0; y < 6; y++ ) {
context3.drawImage(photo3, x * 100, y * 75, 100, 75);
}
}
}
window.addEventListener("load", drawImage, false);
Since you're loading the script in the <head>, everything is running before the DOM is loaded, so all your getElementBuId() calls are failing. You either need to put the <script> tag at the end of the <body>, or put all the code into a window.onload function, e.g.
window.onload = function() {
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
photo = document.getElementById("red");
function drawImage() {
context.drawImage(photo, 0, 0);
}
window.addEventListener("load", drawImage, false);
...
};
This has the added benefit of not polluting the global namespace.
I'll second what Barmar said. In general, I load my JavaScript at the end of the html for better performance, and so I'm sure I won't have this issue.
Related
I found a cool animation on codepen that takes a map (img) and reconstruct it with blocks. The js files needed is three.min.js and TweenMax.min.js I took the links from codepen and pasted it into my head within <script src=""></script>. After copying every css and html (not much) it apears that three.min.js got an error(?).
I opened google chrome console and saw three.min.js:536 Uncaught TypeError: Cannot read property 'width' of null.
heres the codepen animation im reffering to:
http://codepen.io/Mamboleoo/pres/JYJPJr
My code:
<!DOCTYPE html>
<html>
<head>
<?php include $_SERVER["DOCUMENT_ROOT"] . "/assets/head.php"; ?>
<title><?php echo $address; ?> - Credits</title>
</head>
<body>
<?php include $_SERVER["DOCUMENT_ROOT"] . "/navigationbar.php"; ?>
<script>
var renderer, scene, camera, ww, wh, particles;
ww = window.innerWidth,
wh = window.innerHeight;
var centerVector = new THREE.Vector3(0, 0, 0);
var previousTime = 0;
var getImageData = function(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, image.width, image.height);
}
var drawTheMap = function() {
var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({
size: 3,
color: 0x313742,
sizeAttenuation: false
});
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width) + 3] > 128) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 1000 - 500;
vertex.y = Math.random() * 1000 - 500;
vertex.z = -Math.random() * 500;
vertex.destination = {
x: x - imagedata.width / 2,
y: -y + imagedata.height / 2,
z: 0
};
vertex.speed = Math.random() / 200 + 0.015;
geometry.vertices.push(vertex);
}
}
}
particles = new THREE.Points(geometry, material);
scene.add(particles);
requestAnimationFrame(render);
};
var init = function() {
THREE.ImageUtils.crossOrigin = '';
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("map"),
antialias: true
});
renderer.setSize(ww, wh);
renderer.setClearColor(0x1d1f23);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, ww / wh, 0.1, 10000);
camera.position.set(-100, 0, 220);
camera.lookAt(centerVector);
scene.add(camera);
texture = THREE.ImageUtils.loadTexture("http://mamboleoo.be/lab/transparentMap.png", undefined, function() {
imagedata = getImageData(texture.image);
drawTheMap();
});
window.addEventListener('resize', onResize, false);
};
var onResize = function(){
ww = window.innerWidth;
wh = window.innerHeight;
renderer.setSize(ww, wh);
camera.aspect = ww / wh;
camera.updateProjectionMatrix();
};
var render = function(a) {
requestAnimationFrame(render);
for (var i = 0, j = particles.geometry.vertices.length; i < j; i++) {
var particle = particles.geometry.vertices[i];
particle.x += (particle.destination.x - particle.x) * particle.speed;
particle.y += (particle.destination.y - particle.y) * particle.speed;
particle.z += (particle.destination.z - particle.z) * particle.speed;
}
if(a-previousTime>100){
var index = Math.floor(Math.random()*particles.geometry.vertices.length);
var particle1 = particles.geometry.vertices[index];
var particle2 = particles.geometry.vertices[particles.geometry.vertices.length-index];
TweenMax.to(particle, Math.random()*2+1,{x:particle2.x, y:particle2.y, ease:Power2.easeInOut});
TweenMax.to(particle2, Math.random()*2+1,{x:particle1.x, y:particle1.y, ease:Power2.easeInOut});
previousTime = a;
}
particles.geometry.verticesNeedUpdate = true;
camera.position.x = Math.sin(a / 5000) * 100;
camera.lookAt(centerVector);
renderer.render(scene, camera);
};
init();
</script>
<style>
canvas{width:100%;height:100%;padding:0;margin:0;overflow: hidden;}
</style>
<div class="wrapper">
<canvas id="map"></canvas>
</div>
<div style="position:relative; clear:both;"></div>
<!--</body>-->
<?php include $_SERVER["DOCUMENT_ROOT"] . "/footer.php"; ?>
</body>
</html>
The problem was worked out in the comments, but for the sake of not leaving a question technically unanswered I will explain the process for anyone stumbling across this page after searching for the error posted in the question.
In the example which was posted in the question (which is utilizing PHP to load the Javascript, but that matters little for the actual problem at hand) the Javascript relating to ThreeJS is being executed before the DOM has loaded the canvas element. Obviously ThreeJS requires the Canvas element, as it attaches its various listeners and objects to it, so when attempting to access members related to the canvas element it was simply getting undefined.
The fix for this was to load ThreeJS and all code related to it after the DOM had loaded the elements, there are multiple methods of doing that (which you should search for, as I'm afraid I don't have the time to explain them all), but I will highlight one which I deem the easiest. The method is to put all of your DOM specific code (Javascript that interacts with the DOM) at the bottom of your body tag (not below it, inside it at the very bottom. As Vikas Kapadiya pointed out, it would not be valid HTML if it was below it)
The below snippet shows how Javascript is loaded in relation to the DOM:
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
var p = document.getElementById('example');
console.log("In head " + p);
</script>
</head>
<body>
<p id="example">Hello</p>
<script>
var p = document.getElementById('example');
console.log("In body " + p.innerHTML);
</script>
</body>
</html>
Output:
In head null
In body Hello
As dictated by the above, the code within the head tag could not access the innerHTML (refering to the text content of the p tag) due to being executed before the DOM was loaded. The code found at the bottom of the body tag could, as the DOM was loaded and then the browser stumbled upon the Javascript.
Here are some related links which could shed more light than I have:
Where should I put <script> tags in HTML markup?
Where to place JavaScript functions: <head>? <body>? or, after </html>?
Just move wrapper div to just after body tag . then include all js .
Like this
<body>
<div class="wrapper">
<canvas id="map"></canvas>
</div>
I am creating a file called functions.js with diferent functions like:
bgImage()
drawImage()
setText()
My issue is that my text keeps staying behind.
What i want to do is, when i call setText() i can put text where i want. And the text will be put on the top ofc the convas. I know i need to call the image draw load functions first to get them to not overwrite my text. But i did so in my JS.
So its very important that i can call the function setText() as many times as i want, after all images are drawn/set, and the text will be visible.
I want the text on the top.
Here is my code:
functions.js
var canvas = "";
var context = "";
function canvasInit() {
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
}
function bgImage() {
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
function drawImage() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 10, 50);
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
}
function (text) {
context.font = 'italic 40pt Calibri';
context.fillText(text, 150, 100);
}
index.html
<!DOCTYPE html>
<html>
<head>
<script src="functions.js"></script>
<script>
document.addEventListener('DOMContentLoaded',ready);
function ready() {
canvasInit();
bgImage();
drawImage();
setText("Yo");
setText("heyyyy");
}
</script>
</head>
<body>
<canvas id="myCanvas" width="500" height="400"></canvas>
</body>
</html>
Updated test that do not work:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="functions.js"></script>
<script>
document.addEventListener('DOMContentLoaded',ready);
function ready() {
canvasInit();
bgImage(function() {
setText();
});
}
</script>
</head>
<body>
<canvas id="myCanvas" width="500" height="400"></canvas>
</body>
</html>
functions.js
var canvas = "";
var context = "";
function canvasInit() {
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
}
function bgImage(callback) { // add parameter for function
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
if (typeof callback === 'function') {
callback(); // invoke callback function
}
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
function drawImage() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 10, 50);
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
}
function setText() {
context.font = 'italic 40pt Calibri';
context.fillText("Yoo adfa ds asd a sd", 150, 100);
}
It happens because your image loading is asynchronous: before the image has finished loading you draw your text as the function exits after setting the source. Then when the image has finished loaded the onload function is called and the image drawn on top of whatever is drawn previously (in this case the text).
You need to implement a callback handler for your functions for this to work - for example:
function bgImage(callback) { /// add parameter for function
var imageObj = new Image();
imageObj.onload = function() {
var pattern = context.createPattern(imageObj, 'repeat');
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.fill();
if (typeof callback === 'function')
callback(); /// invoke callback function
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/wood-pattern.png';
}
Then you use it:
bgImage(function() {
setText();
});
You will of course need to do this with the other image loading functions as well. Tip: If you end up with a long chain it's probably better to assign non-anonymous functions instead of inline them as in the last example.
Update:
Just for clarity: it's important that the function provided is provided as a reference and not a result from calling, for example: use the callback this way:
bgImage(setText); /// correct
not this way:
bgImage(setText()); /// wrong
With the parenthesis the setText is simply invoked and its result is passed as a callback. This means the text will be drawn first and then bgImage is called.
I never got my code to work. However i found some framework called "http://kineticjs.com/" and i solved my issues using that.
Thanks for the comments Ken - Abdias Software. I did also take a look at what you linked to in your profile description. Looking really neat ;)
However i feel its the best to accept the answer that solved my problem, and i solved it myself.
Thanks again :)
So, I have an <img> tag that has an onclick attribute. The onclick calls a function called analyze(this), with this being the image.
The analyze function does some things to the image that aren't entirely relevant, except for the fact that it draws it onto the <canvas> element (using the drawImage function).
But now, I want to also pick the color I just clicked on in the image. I am currently using the method answered here (the answer with 70+ votes, not the chosen one): How do I get the coordinates of a mouse click on a canvas element?
But, I think I might be doing this wrong. I have the image drawn and my functions called (and those all work), but the color picking part isn't being called. I think that this is because I didn't actually capture the event. This is generally how my code looks:
<img onclick="javascript:analyze(this);" />
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
img = new Image();
img.onload = function () {
canvaselement.drawImage(img, 0, 0, 250, 250);
...
canvaselement.onClick = function () {
var coords = canvaselement.relMouseCoords(event);
pick(img, canvaselement, coords); // pass in coordinates
}
}
img.src = img_elem.src;
}
function relMouseCoords(event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {
x: canvasX,
y: canvasY
}
}
function pick(img, canvaselement, coords) {
var pickedColor = "";
canvaselement.drawImage(img, 0, 0, 250, 250);
xx = coords.x;
yy = coords.y;
var imgData = canvas.getImageData(xx, yy, 1, 1).data;
pickedColor = rgbToHex(imgData);
//alert(pickedColor);
return pickedColor;
}
So, the code never gets to the pick function. I have a feeling that it's because I didn't actually capture the onclick event. I'm also not even sure if this is the right way to get the coordinates on the canvas, I'm just sort of hoping that I even get to that part of the debugging process at this point.
Thanks for your help!
The problem is probably that you're assigning canvaselement to the results of getContext('2d') and not to the element itself, which you will need for the click event binding. Create two variables, one for the DOM element itself and one for the context, something like:
var canvaselement = document.getElementById('canvas'),
canvaselementctx = canvaselement.getContext('2d');
...
canvaselement.onClick = function() {
var coords = canvaselementctx.relMouseCoords(event);
...
}
You have a couple of errors in the code but the reason the code you got from the linked post is that you forgot to include the prototype definition it uses:
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
Now you can call relMouseCoords on the canvas element:
/// event name in lower case
canvaselement.onclick = function () {
var coords = canvaselement.relMouseCoords(event);
//...
However, you will still get problems as you don't use a canvas context for the drawing calls.
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
/// get context like this
ctx = canvaselement.getContext('2d'),
img = new Image();
img.onload = function () {
/// use context to draw
ctx.drawImage(img, 0, 0, 250, 250);
//...
I am a newbie in HTML5 but have good experience of HTML. I was learning about canvas and thought of making a program. In this I was handling the user's mousedown and mouseup and was setting up the values of my variables according to the coordinates of the mouse. Then with the help of those I was stroking the line on the canvas which is not being drawn properly.
The work I have done to achieve this:
HTML
<script type="text/javascript" src="jquery.min.js"></script>
<script src="bhaiya.js"></script>
<canvas id="myCanvas" style="height: 100%; width: 100%;">
</canvas>
JS
$(document).ready(function() {
var $x1 = 0;
var $x2 = 0;
var $y1 = 0;
var $y2 = 0;
$(this).mousedown(function(e){
$x1 = e.pageX;
$y1 = e.pageY;
});
$(this).mouseup(function(e){
$x2 = e.pageX;
$y2 = e.pageY;
var c = document.getElementById("myCanvas");
var context = c.getContext("2d");
context.moveTo($x1, $y1);
context.lineTo($x2, $y2);
context.stroke();
});
});
What is the problem? Any help would be appreciated! :)
1) If you're bothered by the fact the drawing is fuzzy and doesn't seem to follow the x and y you give, then you can fix it like this :
var c = document.getElementById("myCanvas");
c.width = c.clientWidth;
c.height = c.clientHeight;
2) you must take into account the offset due to the canvas position when it's not exactly in the top-left corner of the document :
$x1 = e.pageX-c.offsetLeft;
$y1 = e.pageY-c.offsetTop;
Demonstration
Note that in a real application you shouldn't recreate the context each time. In this case, you would also begin a new path when needed (probably at each click).
Use .beginPath():
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
http://jsfiddle.net/j4XY8/
http://dev.opera.com/articles/view/html5-canvas-painting/ (might be helpful)
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I would like to add to a drawing program buttons with different functions. But I got problems with the first one, of course. I'm trying to have a button to clear the entire canvas. But somehow it doesn't work.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var cb_canvas = null;
var cb_ctx = null;
var cb_lastPoints = null;
var cb_easing = 0.4;
// Setup event handlers
window.onload = init;
function init(e) {
cb_canvas = document.getElementById("cbook");
cb_lastPoints = Array();
if (cb_canvas.getContext) {
cb_ctx = cb_canvas.getContext('2d');
cb_ctx.lineWidth = 2;
cb_ctx.strokeStyle = "rgb(0, 0, 0)";
cb_ctx.beginPath();
cb_canvas.onmousedown = startDraw;
cb_canvas.onmouseup = stopDraw;
cb_canvas.ontouchstart = startDraw;
cb_canvas.ontouchstop = stopDraw;
cb_canvas.ontouchmove = drawMouse;
}
}
function startDraw(e) {
if (e.touches) {
// Touch event
for (var i = 1; i <= e.touches.length; i++) {
cb_lastPoints[i] = getCoords(e.touches[i - 1]); // Get info for
finger #1
}
}
else {
// Mouse event
cb_lastPoints[0] = getCoords(e);
cb_canvas.onmousemove = drawMouse;
}
return false;
}
// Called whenever cursor position changes after drawing has started
function stopDraw(e) {
e.preventDefault();
cb_canvas.onmousemove = null;
}
function drawMouse(e) {
if (e.touches) {
// Touch Enabled
for (var i = 1; i <= e.touches.length; i++) {
var p = getCoords(e.touches[i - 1]); // Get info for finger i
cb_lastPoints[i] = drawLine(cb_lastPoints[i].x, cb_lastPoints[i].y,
p.x, p.y);
}
}
else {
// Not touch enabled
var p = getCoords(e);
cb_lastPoints[0] = drawLine(cb_lastPoints[0].x, cb_lastPoints[0].y, p.x,
p.y);
}
cb_ctx.stroke();
cb_ctx.closePath();
cb_ctx.beginPath();
return false;
}
// Draw a line on the canvas from (s)tart to (e)nd
function drawLine(sX, sY, eX, eY) {
cb_ctx.moveTo(sX, sY);
cb_ctx.lineTo(eX, eY);
return { x: eX, y: eY };
}
// Get the coordinates for a mouse or touch event
function getCoords(e) {
if (e.offsetX) {
return { x: e.offsetX, y: e.offsetY };
}
else if (e.layerX) {
return { x: e.layerX, y: e.layerY };
}
else {
return { x: e.pageX - cb_canvas.offsetLeft, y: e.pageY -
cb_canvas.offsetTop };
}
}
$("clear").onclick=function(){clearAll()};
function clearAll() {
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
</script>
</head>
<body>
<canvas id="cbook" width="500" height="333"></canvas>
<button id="clear">Clean Up</button>
</body>
</html>
You are pretty much right, there are a couple of problems...
$("clear").onclick=function(){clearAll()};
Should probably read
$("#clear").click(clearAll);
You needed to mark the selector as an id and also you were trying to put a native element event on a jQuery object.
The other problem is similar you are using the jQuery object containing a canvas instead of the native element, instead change...
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
To...
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.get(0).width, canvas.get(0).height);
Or more simply...
var canvas = $("#cbook").get(0);
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
A fiddle...
http://jsfiddle.net/zZhfE/
Oh and totally forgot, the window.onload handler is unnecessary as you are running it within the document ready function of jQuery everything should be ready to go anyway. I just moved everything out of the function and removed the window.onload handler.
I can see three main issues with your code. First, you're mixing and matching Javascript and jQuery, and it's causing issues (when working with Canvas, try and stick to vanilla Javascript).
You're not getting the context in the clearAll() function because you're trying to use jQuery:
var canvas = $("#cbook");
When you should be using plain ol' JS, like in your drawing function:
var canvas = document.getElementById("cbook");
Secondly, you're calling the clear function like this:
$("clear").onclick=function(){clearAll()};
When it should be this:
$("#clear").on('click', function(){clearAll()});
(note the # before clear - you need to use this to refer to the ID of an element.)
Finally, you're looking for the first instance of a canvas inside a canvas, which won't work:
var ctx = canvas.get(0).getContext("2d");
All you need is:
var ctx = canvas.getContext("2d");
Here's a working JSFiddle:
http://jsfiddle.net/hGjDR/