How to dynamically alternate between a canvas background color and an image in JS? - javascript

I have a webGL program where I create lights in my scene stored in an array. When I call render, I'm trying to make the background be black when there are no lights in the array and have an image instead if there are lights in the array.
The code in question is below:
if(lights.length == 0) {
canvas.style.background = "#000000 none";
} else {
canvas.style.background = "#00000000 url('nebula.png')";
}
Right now, everything appears black when I start the program (which is correct since there are no initial lights present), but when I add a light, the background doesn't change to an image and continues to be black.
I have debugged my program and, as the next image shows, I can access both the if and else clauses:
let lights = [];
let counter = 0;
function setup(shaders)
{
let canvas = document.getElementById("gl-canvas");
let aspect = canvas.width / canvas.height;
window.requestAnimationFrame(render);
function resize_canvas(event)
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
aspect = canvas.width / canvas.height;
}
function render()
{
window.requestAnimationFrame(render);
if(lights.length == 0) {
canvas.style.background = "#000000 none";
} else {
canvas.style.background = "#FF0000 none";
}
if(counter%2 == 0) {
lights.push(0);
} else {
lights.pop();
}
counter++;
}
}
setup();
body, html {
height: 100%;
}
body {
/*display: flex;*/
margin: 0px;
overflow: hidden; /* Disable scrollbars */
background-color: black;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
<title>LightStudio</title>
<script type="module" src="./app.js"></script>
</head>
<body>
<canvas id="gl-canvas" width="128" height="128">
Oops! Something went wrong!
</canvas>
</body>
</html>
The above snippet has a similar behaviour to my program, but here I simulate the adittion and removal of the lights every time I call render, supposed to change between blue and red, which it works. The code also works in my program. But when I change the else clause to "upload" my image, it doesn't work.
Update: when changing the code in a way so the image appears when there are no lights, the image appears when the program begins, as intended, and changes to black when adding a light. But when I try to remove the light, the screen stays black and the image doesn't appear anymore.
if(lights.length == 0) {
console.log("if")
canvas.style.background = "#00000000 url('nebula.jpg')"
} else {
console.log("else")
canvas.style.background = "#000000 none";
}

Related

Trigger event when ALL of an element's pixels are transparent [duplicate]

This question already has an answer here:
Is the canvas empty?
(1 answer)
Closed 1 year ago.
I have a web page with two canvas elements stacked on top of each other. This is the basis for some functionality that lets me "erase" the top canvas to reveal an image loaded into the bottom canvas. The functionality works well.
What I'm trying to do now is trigger an event once the top canvas has been completely "erased" i.e. all of the pixels for the top context are transparent. I've found ways on SO to check a particular pixel's alpha value via getImageData but I'm trying to figure out a way to determine when the very last pixel's alpha value = 0.
I've included my code as well as an unfinished attempt to do this with a for and if loop (but this seems to not be the best approach). Another issue I've discovered is that when I use getImageData a thin grey border appears around two edges of my canvas.
Thanks for any help in advance!
window.onload = function() {
//Move speaker image across page
var speaker = document.getElementById('speaker');
speaker.onload = MoveElement(speaker, "right", 1000);
//Create canvases & contexts
var canvas = document.getElementById('canvas');
var ctxB = canvas.getContext('2d');
var canvas2 = document.getElementById('canvas2');
var ctxT = canvas2.getContext('2d');
//Get waterfall image object
var waterfall = document.getElementById('waterfall');
//Set canvas w&h properties
canvas.width = canvas2.width = .3*waterfall.width;
canvas.height = canvas2.height = .3*waterfall.height;
//Populate Bottom canvas with waterfall image
ctxB.drawImage(waterfall, 0, 0, canvas.width, canvas.height);
//Populate Top canvas with white rectangle
ctxT.fillStyle = "white";
ctxT.fillRect(0, 0, canvas2.width, canvas2.height);
//Make Top canvas "erasable"
canvas2.addEventListener('mousemove', event => {
var x = event.offsetX;
var y = event.offsetY;
const eraseSize = 125;
ctxT.clearRect(x-eraseSize/2, y-eraseSize/2, eraseSize, eraseSize);
})
//Attempt to trigger an event when all pixels of top canvas have alpha = 0
var imageDataTop = ctxT.getImageData(0, 0, canvas2.width, canvas2.height);
console.log(imageDataTop);
for (var i = 0; i < imageDataTop; i++) {
if (imageDataTop.data[i] = 0) {
//
}
}
}
function PlayAudio() {
document.getElementById('boyfriend').play();
}
//Move an element in a given direction a certain distance
function MoveElement(element, direction, totalDistance) {
//Determine horizontal or vertical direction
var LeftOrTop = (direction=="left" || direction=="right") ? "left" : "top";
//Set default frame distance
var frameDistance = 150;
//Adjust for backwards direction
if (direction=="left" || direction=="up") {
totalDistance *= -1;
frameDistance = -1;
}
//Get style data object for element
var elementStyle = window.getComputedStyle(element);
//Get Left or Top position of element
var positionValue = elementStyle.getPropertyValue(LeftOrTop).replace("px", "");
//Apply direction and distance change to element
var destination = (Number(positionValue) + totalDistance) + "px";
function MoveFrame() {
//If element reaches its destination...
if (elementStyle.getPropertyValue(LeftOrTop) == destination) {
//...stop the interval motion
clearInterval(movingFrames);
}
//Otherwise, continue the interval movement
else {
elementStyle = window.getComputedStyle(element);
positionValue = elementStyle.getPropertyValue(LeftOrTop).replace("px", "");
element.style[LeftOrTop] = (Number(positionValue) + frameDistance) + "px";
}
}
//Define time interval for movement
var movingFrames = setInterval(MoveFrame, 500);
}
#stack {
position: relative;
}
#stack canvas {
position: absolute;
display: block;
left: 50%;
transform: translateX(-50%);
margin-top: 150px;
}
#speaker {
position: absolute;
top: 20px;
animation-duration: 800ms;
animation-name: fadein;
animation-iteration-count: 10;
}
#keyframes fadein {
from {
opacity: 0.25;
}
50% {
opacity: 1;
}
to {
opacity: 0.25;
}
}
<!DOCTYPE html>
{% load static %}
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="{% static 'entrance/entrance.css' %}">
<script src="{% static 'entrance/entrance.js' %}"></script>
</head>
<body>
<p hidden>
<img src="{% static 'entrance/Waterfall.jpg' %}" alt="issue here" id="waterfall" />
</p>
<img src="{% static 'entrance/sound-icon.svg' %}" id="speaker" />
<p id="test"></p>
<audio id="boyfriend" source src="{% static 'entrance/02 30 Minute Boyfriend.mp3' %}" type="audio/mp3"></audio>
<div id="stack" onmouseenter="PlayAudio()">
<canvas id="canvas"></canvas>
<canvas id="canvas2"></canvas>
</div>
</body>
</html>
The dataimage that you get will be an array which has 4 bytes per point on the canvas the first 3 bytes of which are the RGB colors and the 4th one is the alpha.
If the color is transparent this 4th one will be 0. You will need to go through each time some erasing is done looking at every 4th byte in the array. If you come across one that isn't zero you will know that the erasing isn't complete.
This is quite a useful reference: https://www.w3schools.com/tags/canvas_getimagedata.asp

How to flip an image in javascript on a html canvas?

I am making a game in pure js with an HTML5 canvas. My player is a cube but its weapons are images. I have successfully drawn the image on top of the player:
var gun = document.getElementById('gun');
c.drawImage(gun,this.x,this.y);
but I want it to flip the gun image vertically when the player turns. I have tried changing scaleX when the player turns:
if(this.l == true){
this.x -= this.speed;
gun.style.transform = 'scaleX(-1)';
}
if(this.r == true){
this.x += this.speed;
gun.style.transform = 'scaleX(1)';
}
but nothing happens, please help
the HTML :
<html>
<head>
<title>CUBE</title>
<style>
body{
margin: 0px;
}
</style>
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
</head>
<body>
<canvas id='canvas'></canvas>
<img id="gun" style ='display: none;' src="gun.png">
<script src = 'index.js'></script>
</body>
Instead of using css for scaling I would go for the following solution:
function goLeft() {
gun.flipX = true
}
function goRight() {
gun.flipX = false
}
function drawGun() {
if(gun.flipX) {
ctx.drawImage(gunImg, -gun.x -gun.w,gun.y,gun.w,gun.h)
}
else {
ctx.drawImage(gunImg,gun.x,gun.y,gun.w,gun.h)
}
}

Draw rectangle over uploaded image

Once I upload an image I make 2 clicks over it. That's how (x1,y1) and (x2,y2) are obtained. Given these 4 numbers I waanna draw a rectangle over the image by means for example of P5.js. Then I should say rect(x1,y1,x2,y2) but this never happens. How can I deal with this problem (maybe not by P5)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue Test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<button v-on:click="isHidden = false">Load img</button>
<img onclick="showCoords(event)" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
<script>
var test = new Vue({
el: '#app',
data: {
src: ["cat.jpg", "dog.jpg"],
z: Math.round(Math.random()),
isHidden: true,
}
})
var k=0;
var koors = [];
var flag = false;
function showCoords(event) {
if (k===0){
koors[0] = event.clientX;
koors[1] = event.clientY;
k+=1;
} else if (k===1){
flag = true;
koors[2] = event.clientX;
koors[3] = event.clientY;
}
if ((koors[3] != 0) && (flag)){
console.log(koors)
}
}
//p5
function setup(){
}
function draw(){
console.log(koors[0],koors[1],koors[2],koors[3]);
rect(koors[0],koors[1],koors[2],koors[3])
}
</script>
<script src="p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="js.js"></script>
</body>
</html>
P5 needs a canvas for rendering. If you don't initialize one, it creates it himself (and you are in trouble).
Also, P5 is powerful library, it has tools for events, image processing etc. For current example I wouldn't use any other library (vue).
I created canvas on top of the image (css) and rest is playing with P5.
var test = new Vue({
el: '#app',
data: {
src: [ "https://i.stack.imgur.com/XZ4V5.jpg", "https://i.stack.imgur.com/7bI1Y.jpg"],
z: Math.round(Math.random()),
isHidden: false,
}
})
var recStart;
var coords = {};
/*******************************************************************
* P5 setup
* run once, use for initialisation.
*
* Create a canvas, change dimensions to
* something meaningful(like image dim)
********************************************************************/
function setup(){
var canvas = createCanvas(480, 320);
canvas.parent('app');
}
/*******************************************************************
* P5 draw
* endless loop.
*
* Lets redraw rectangle until second click.
********************************************************************/
function draw(){
if(recStart)
drawRect();
}
/*******************************************************************
* drawRect
*
* Draw a rectangle. mouseX and mouseY are P5 variables
* holding mouse current position.
********************************************************************/
function drawRect(){
clear();
noFill();
stroke('red');
rect(coords.x, coords.y, mouseX-coords.x, mouseY-coords.y);
}
/*******************************************************************
* P5 mouseClicked
*
* P5 event. Again, mouseX and mouseY are used.
********************************************************************/
mouseClicked = function() {
if (mouseButton === LEFT) {
if(!recStart){ // start rectangle, give initial coords
coords.x = mouseX;
coords.y = mouseY;
recStart = true; // draw() starts to draw
} else {
recStart = false; // stop draw()
drawRect(); // draw final rectangle
coords = {}; // clear coords
}
}
};
canvas {
width: 500px;
height:500px;
z-index: 2;
border:1px solid;
position:absolute;
left: 0;
top: 0;
}
img {
z-index: 1;
position: absolute;
border: 2px solid black;
left: 0;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<img id="pilt" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
It seems that you don't call draw anywhere. You may also consider draw the image into a canvas and than draw rect in this canvas. Checking if koors[3] != 0 is really risky as user may click at 0th pixel.

How do I remove an already drawn image from HTML5 Canvas (just that one not other ones)

So, I have to make this project for school and I need to be able to remove one image specifically because when I do clearRect() it removes all images.
<!DOCTYPE html>
<html>
<head>
<title>Game</title>
<style type="text/css">
* { margin:0; padding:0; } /* to remove the top and left whitespace */
html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
canvas { display:block; } /* To remove the scrollbars */
#gC {
position: relative;
left: 16vw;
top: 9vh;
margin: 0;
padding: 0;
}
body {
background-image: url('bg.jpeg') !important;
overflow: hidden !important;
}
</style>
</head>
<body> <canvas id="gC" width="1500" height="1000"></canvas>
<script type="text/javascript">
var canvas = document.querySelector("#gC");
var ctx = canvas.getContext("2d");
var team1 = new Image();
var bT = new Image();
var t1_x = 150;
var t1_y = 100;
var bT_x = 50;
var bT_y = 350;
var AI_I = 0;
team1.src = "team1.png";
bT.src = "bT.png";
function team1AI() {
ctx.clearRect(0, 0, canvas.height, canvas.width);
ctx.drawImage(bT, bT_x, bT_y);
ctx.drawImage(team1, t1_x, t1_y);
t1_x++;
}
var t1AI = setInterval(team1AI,10);
function t1AI_inter(){
if(AI_I >= 90){
clearInterval(t1AI);
}
AI_I++;
}
function leftArrowPressed(){
ctx.clearRect(0, 0, canvas.height, canvas.width);
ctx.drawImage(bT, bT_x, bT_y);
bT_x-=5;
}
function moveSelection(evt) {
switch (evt.keyCode) {
case 37:
leftArrowPressed();
break;
case 39:
rightArrowPressed();
break;
case 38:
upArrowPressed();
break;
case 40:
downArrowPressed();
break;
/*
case 32:
spaceKeyPress();
break;
*/
}
}
setInterval(t1AI_inter,10);
window.addEventListener('keydown', moveSelection);
</script>
Games are not (usually) based on events like "keydown". They are based on loops, one loop of frames. In every frame or iteration, you should, for example,
read the user input
update the status of every component of your game
draw the new frame
Anyway, if you want to remove just one image, just remove all of them and redraw just the ones that should be visible.
You could have an array with the status (visible or not) of every image or something like this:
var images = [
{visible: false},
{visible: true},
{visible: true}
];
You could also use a function "draw" which draws the images based in the previous array and another two functions called "hide" and "show", which could receive the index of the image to hide or show, update its status in the array and then call the draw function.
If you want to go ahead, read about game loops and specially about the requestAnimationFrame function, which is used in web game development.
I just figured it out! All I had to do is:
ctx.clearRect(t1_x,t1_y,canvas.height,canvas.width);

Drawing repeatedly updating images to the canvas (Safari blanks out image during loading)

I'm drawing an image onto the background of a canvas. This image refreshes every second, but the canvas needs to be redrawn more often than that, every 0.5 seconds. The problem I seem to be running into is that Safari flickers when updating the canvas because it appears to lose the image data from the bgImg variable. Firefox works as expected and no flicker occurs.
Does anyone have any clue as to how to avoid this? Am I missing something really obvious?
To test this code, simply paste the entire code below into an html file and load with your browser. What it should do is:
create a canvas
load an image every second
redraw the canvas every 500ms, using the image as a background.
Here's the code:
<html>
<head>
<script type="text/javascript">
var bgImg = new Image();
var firstload = false;
var cv;
var ctx;
var updateBackground = function() {
bgImg.onload = function() {
firstload = true;
console.log("image loaded. Dimensions are " + bgImg.width + "x" + bgImg.height);
}
bgImg.src = "http://mopra-ops.atnf.csiro.au/TOAD/temp.php?" + Math.random() * 10000000;
setTimeout(updateBackground, 1000);
}
var initGraphics = function() {
cv = document.getElementById("canvas");
ctx = cv.getContext("2d");
cv.width = cv.clientWidth;
cv.height = cv.clientHeight;
cv.top = 0;
cv.left = 0;
}
var drawStuff = function() {
console.log("Draw called. firstload = " + firstload );
ctx.clearRect(0, 0, cv.width, cv.height);
if ( firstload == true) {
console.log("Drawing image. Dimensions are " + bgImg.width + "x" + bgImg.height);
try {
ctx.drawImage(bgImg, 0, 0);
} catch(err) {
console.log('Oops! Something bad happened: ' + err);
}
}
setTimeout(drawStuff, 500);
}
window.onload = function() {
initGraphics();
setTimeout(updateBackground, 1000);
setTimeout(drawStuff, 500);
}
</script>
<style>
body {
background: #000000;
font-family: Verdana, Arial, sans-serif;
font-size: 10px;
color: #cccccc;
}
.maindisplay {
position:absolute;
top: 3%;
left: 1%;
height: 96%;
width: 96%;
text-align: left;
border: 1px solid #cccccc;
}
</style>
</head>
<body>
<div id="myspan" style="width: 50%">
<canvas id="canvas" class="maindisplay"></canvas>
</div>
</body>
</html>
Your problem is that you are drawing bgImg while it's loading. Webkit does not cache the old image data while loading a new image; it wipes out the image as soon as a new .src is set.
You can fix this by using two images, using the last-valid-loaded one for drawing while the next one is loading.
I've put an example of this online here: http://jsfiddle.net/3XW8q/2/
Simplified for Stack Overflow posterity:
var imgs=[new Image,new Image], validImage, imgIndex=0;
function initGraphics() {
// Whenever an image loads, record it as the latest-valid image for drawing
imgs[0].onload = imgs[1].onload = function(){ validImage = this; };
}
function loadNewImage(){
// When it's time to load a new image, pick one you didn't last use
var nextImage = imgs[imgIndex++ % imgs.length];
nextImage.src = "..."+Math.random();
setTimeout(loadNewImage, 2000);
}
function updateCanvas(){
if (validImage){ // Wait for the first valid image to load
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(validImage, 0, 0);
}
setTimeout(updateCanvas, 500);
}

Categories