I have written an A-Frame component that creates a canvas on which it writes an image. The image is generated dynamically from HTML. The HTML is created when a message arrives from a server, using WebSockets, asynchronously. The problem is that most often (but not always) the canvas is drawn blank. I have tested the component using hardcoded HTML, and it works just as expected, so the issue must be one of timing, but I don't know why or how to solve it.
Schematically, the code is:
In a JS module file, vr.js:
// Observe the arrival of the websocket message, then
// Create the HTML based on the message and save it as 'legend'
let legendBox = document.getElementById('legend')
legendBox.innerHTML = legend
legendBox.components.showHTML.update();
In the HTML file:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../js/vr.js" type="module"></script>
AFRAME.registerComponent('showHTML', {
init: function () {},
update: function () {
if (this.el.getObject3D('screen')) this.remove()
if (this.el.innerHTML.trim() === '') return
this.htmlcanvas = new HTMLCanvas(this.el)
let texture = new THREE.CanvasTexture(this.htmlcanvas.canvas)
texture.minFilter = THREE.LinearFilter
texture.wrapS = THREE.ClampToEdgeWrapping
texture.wrapT = THREE.ClampToEdgeWrapping
let material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
})
let geometry = new THREE.PlaneGeometry(1, 1)
let screen = new THREE.Mesh(geometry, material)
this.el.setObject3D('screen', screen)
this.screen = screen
this.resize()
},
resize() {
this.width = this.htmlcanvas.width / this.data.ppu
this.height = this.htmlcanvas.height / this.data.ppu
this.screen.scale.x = Math.max(0.0001, this.width)
this.screen.scale.y = Math.max(0.0001, this.height)
this.htmlcanvas.render()
},
remove: function () {
this.el.removeObject3D('screen')
},
})
class HTMLCanvas {
constructor(html) {
if (!html) throw 'Container Element is Required'
// Create the canvas to be drawn to
this.canvas = document.createElement('canvas')
this.ctx = this.canvas.getContext('2d')
// code to extract the innerHTML from the entity, transform it into an SVG,
// and load the SVG into an image -- all omitted for simplicity
// Renders the image containing the SVG to the Canvas
render() {
this.canvas.width = this.width
this.canvas.height = this.height
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.ctx.drawImage(this.img, 0, 0)
}
} // end class
} // end of component
</head>
<body>
<a-scene>
// usual A-Frame entities omitted
<a-entity id="legend" position="-1 1 -2" showHTML></a-entity>
// more A-Frame stuff including camera etc.
</a-scene>
</body>
</html>
Related
I am currently experimenting with Three.js but I can't seem to fix this error, I've tried and searched for a lot of answers but I cant fix it.
I've included the threejs library
and in my script everything is written just fine
This is my html index file
<body>
<script src="script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/242637/TrackballProjectorDetector.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/242637/AsciiEffect.js"></script>
</body>
This is some part of the script file
function init() {
var width = window.innerWidth || 2;
var height = window.innerHeight || 2;
container = document.createElement('div');
document.body.appendChild(container);
var info = document.createElement('div');
info.style.position = 'absolute';
info.style.top = '20px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'Drag to change the view';
container.appendChild(info);
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
camera.position.y = 150;
camera.position.z = 500;
controls = new THREE.TrackballControls(camera);
scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff);
light.position.set(500, 500, 500);
scene.add(light);
var light = new THREE.PointLight(0xffffff, 0.25);
light.position.set(-500, -500, -500);
scene.add(light);
sphere = new THREE.Mesh(new THREE.SphereGeometry(200, 20, 10), new THREE.MeshLambertMaterial());
scene.add(sphere);
I get an error on the line:
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
But when I remove it, the error goes down to the next THREE element.
<script src="script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
I'd say the order may be important here. If the init function is run when loading script.js(a), THREE will not yet have been defined, since that would happen on the following line.
The solution, in that case, would be to load three.js before your script file.
(a) This is often done at the end of a js file but we can't be sure in this case, since we don't have the entire file. Hence I'd be looking at that file to see if it's something like:
function init() {
blahBlahBlah();
}
init();
I am trying to create super mario with javascript but I am getting an error.
I don't understand why I am getting this error please help me out.
I wasn't getting this error before I added a class and const sprite.
I think it has something to do with the const sprite and class SpriteSheet.
I have attached screen shot of that error and I have also entered the whole code.
// A Function That loads image
function loadImage(url){ // give pram of url
return new Promise(resolve => { // create a promise
const image = new Image(); // set image to IMAGE() function
image.addEventListener('load', () => { // add eventlistener to load that image
resolve(image); // resolve image
});
image.src= url; // use param and src to enter url
});
}
// Class SpriteSheet
class SpriteSheet{
constructor(image, width, height){
this.image = image;
this.height = height;
this.width = width;
this.tiles = new Map();
}
// define a buffer so we don't have to create same element again and again.
define(name,x,y){
const buffer = document.createElement('canvas');
buffer.width = this.width;
buffer.heigth = this.height;
buffer.getContext('2d').drawImage(
this.image,
x * this.height,
y * this.width,
this.height,
this.width,
0,
0,
this.width,
this.height);
this.tiles.set(name,buffer);
}
draw(name, x, y){
const buffer = this.tiles.get(name);
context.drawImage(buffer,x,y);
};
}
// Draw a Canvas
const canvas = document.getElementById('screen');
const context = canvas.getContext('2d');
// create a Rectange
context.fillRect(0, 0, 500, 500);
// Define
const sprites = new SpriteSheet(image, 16, 16);
sprites.define('ground', 0,0);
sprites.draw('ground', context, 45,62);
// Load Image
loadImage('/img/tiles.png')
.then(image => {
context.drawImage(image,0,0,16,16,0,0,16,16); // (img,sx,sy,swidth,sheight,x,y,width,height) // First Four means subset you want to draw and other four means where you want to draw it
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Super Mario</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="\js\main.js" type="module"></script>
</head>
<body>
<canvas id="screen" width="640" height="640"></canvas>
</body>
</html>
A promise takes two arguments: resolve and reject.
function loadImage(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener('load', () => resolve(image));
image.addEventListener('error', () => reject(image));
image.src = url;
});
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<canvas id="spriteCanvas" width="500" height="500">
<img id="coin" width="440" height="40" src="coin.png">
</canvas>
</body>
</html>
I tried placing an image inside a canvas element, but it won't display on the browser. I know the image tag works because it's displayed if I place it outside of the canvas element. Also, if I inspect the canvas element, I can see that the image is inside, but its dimensions are 0 by 0. Can somebody explain why this isn't working?
EDIT: My original code added the image through javascript, but it wouldn't show on the canvas. It was giving me the same problems as above. But I just realized I was missing "onload".
original code:
var coinImage = new Image();
coinImage.src = "coin.png";
var sCanvas = document.getElementById('spriteCanvas');
function Sprite(canvas, width, height, image){
this.context = canvas.getContext("2d");
this.width = width;
this.height = height;
this.image = image;
}
Sprite.prototype.render = function() {
var that = this;
this.context.drawImage(that.image, 0, 0);
}
function init() {
var coin = new Sprite(sCanvas, 100, 100, coinImage);
coin.render();
}
init();
editted code:
var coinImage = new Image();
coinImage.src = "coin.png";
var sCanvas = document.getElementById('spriteCanvas');
function Sprite(canvas, width, height, image){
this.context = canvas.getContext("2d");
this.width = width;
this.height = height;
this.image = image;
}
Sprite.prototype.render = function() {
var that = this;
this.context.drawImage(that.image, 0, 0);
}
coinImage.onload = function () {
var coin = new Sprite(sCanvas, 100, 100, coinImage);
coin.render();
}
This isn't how a <canvas> tag works. If you want your image to appear in your canvas, you will have to use JavaScript to place the pixels of the image into your canvas.
<canvas>s are exactly what they state: canvases. They are an element for you to draw on programmatically. If you just want to display an image on a page, you don't need a canvas, you just need the <img> tag. In fact, elements should not be placed in <canvas>.
Take a look at CanvasRenderingContext2D.drawImage() and this tutorial: HTML5 Canvas Image Tutorial.
And this snippet:
var canvas = document.getElementById("painting"),
ctx = canvas.getContext("2d"),
image = new Image();
image.onload = function() {
ctx.drawImage(image, 30, 50);
};
image.src = "http://cdn.sstatic.net/Sites/stackoverflow/img/sprites.png?v=10a9e8743fb0";
<canvas id="painting" width="300" height="300"></canvas>
To draw an image on a canvas, use the following method:
drawImage(image,x,y)
If the image you want to draw is not in the DOM already you can load an image directly from a URL with a few lines of javascript.
function loadAndDrawImage(url)
{
// Create an image object. This is not attached to the DOM and is not part of the page.
var image = new Image();
// When the image has loaded, draw it to the canvas
image.onload = function()
{
// draw image...
}
// Now set the source of the image that we want to load
image.src = url;
}
loadAndDrawImage("http://www---.png");
I am trying to add a canvas to my index.html but it's not working at all. As in it doesn't show up. Here is my index.html and game.js file source. Please help, I've been trying to figure this out for the last hour or so and it still doesn't work. I've tested it in multiple browsers. I'm using brackets as my editor if that helps.
index.html
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dogepet</title>
</head>
<body>
<center><p>Dogepet</p>
<script type="text/javascript" src="js/game.js"></script></center>
</body>
</html>
game.js
//Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 612;
canvas.height = 480;
document.body.appendChild(canvas);
//Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/dogepark.png";
var doge = {
speed: 10;
x: canvas.width/2;
y: 380;
};
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
};
var main = function () {
render();
};
setInterval(main,1);
Look into the Javascript console. In Firefox, I see this error message
[22:39:46.543] SyntaxError: missing } after property list
After replacing the semicolons ; with commas ,, everything works as expected.
See JSFiddle
I'm experimenting with the canvas element, but for some reason it doesn't display my image.
I use the following code:
function Canvas() {
this.field = function() {
var battlefield = document.getElementById('battlefield');
var canvas = battlefield.getElementsByTagName('canvas')[0];
return canvas.getContext('2d');
}
}
function Draw(canvas) {
if (!canvas) {
alert('There is no canvas available (yet)!');
return false;
}
this.canvas = canvas;
this.grass = function(pos_x, pos_y) {
var terrain = new Terrain();
var specs = terrain.grass();
var img = new Image();
img.onload = function(){
canvas.drawImage(img, specs.position_x, specs.position_y, specs.dimension_x, specs.dimension_y, pos_x, pos_y, specs.dimension_x, specs.dimension_y);
console.log('success???'); // this is being output
};
img.src = '/img/terrain.png';
}
}
(function() {
var canvas = new Canvas();
var draw = new Draw(canvas.field());
draw.grass();
})();
There are no errors, but the image just doesn't display. I've verified if the image exists and it does. I also verified the specs and they do contain what they should:
dimension_x: 25
dimension_y: 25
position_x: 0
position_y: 0
Any idea what I'm doing wrong?
http://jsfiddle.net/uwkfD/3/
pos_x and pos_y are undefined in your code. It works if you pass values for them to draw.grass():
draw.grass(0, 0);
DEMO
Tested in Chrome and Firefox.
This may be because you need a context to draw on.
Try:
this.context = canvas.getContext('2d')
Then call your draw functions on the context object not the canvas object:
this.context.drawImage(img, specs.position_x, specs.position_y, specs.dimension_x, specs.dimension_y, pos_x, pos_y, specs.dimension_x, specs.dimension_y);
It also appears that your reference to canvas is wrong too, it should be this.canvas (or this.context) not just canvas, at least as I understand scope in javascript.