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

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)
}
}

Related

Why do buttons on mobile browsers appear to have a larger click area?

At work, they needed a quick and dirty example to hand-sign a document and append it to JSON as a base64 Data URL. It works all fine and dandy in itself, but my own curiosity got the better of me and I was annoyed to find out, that the area of the buttons appeared to be much larger on mobile than they looked like. But even inspecting buttons in my example project didn't help me a lot to pin down the problems.
Disclaimer: I have to also point out, for people who look for a similar solution to NOT just copy-paste this example here. It's serviceable and works probably in 99% of the cases just fine for this purpose, but the drawing is not optimal. Why? Because I keep adding points indefinitely as long as the finger touches the canvas... and with each move, it goes through the array and REDRAWS all the lines! It becomes pretty noticeable after you covered a fair portion of the small canvas. It would be more elegant to go through the tracked points and kick them out of the array once drawn and not only when we raise the finger from the touchscreen. But if you merely use it to sign something like in my example, the example should be perfectly serviceable with no frills.
I did add a snippet as well. Don't forget, it's only working on mobile browsers or if you use emulation on your chrome browser, for example.
"use strict";
const canvas = document.getElementById('sign');
const context = canvas.getContext('2d');
context.lineCap = 'round';
context.lineJoin = 'round';
context.strokeStyle = 'black';
context.lineWidth = 2;
let points = [];
let isPainting = false;
setup();
function setup() {
if (isMobile() || isIpad()) {
setupMobile();
} else {
// Some other stuff that was supposed to happen, but was canned
return;
}
}
function setupMobile() {
canvas.addEventListener('touchstart', (e) => {
addToDrawingPointsTouch(canvas, e);
if (!isPainting) {
isPainting = !isPainting;
}
const ctx = context;
// Not the most elegant solution for the initial call, but touchmove takes a bit to trigger
const initialStarting = points[0];
ctx.beginPath();
ctx.moveTo(initialStarting.x, initialStarting.y);
ctx.lineTo(initialStarting.x, initialStarting.y);
ctx.stroke();
}, { passive: true })
canvas.addEventListener('touchmove', (e) => {
if (isPainting) {
addToDrawingPointsTouch(canvas, e);
const ctx = context;
points.forEach((v, index) => {
if (points[index + 1]) {
ctx.beginPath();
ctx.moveTo(v.x, v.y);
ctx.lineTo(points[index + 1]?.x, points[index + 1]?.y);
ctx.stroke();
}
})
}
}, { passive: true })
canvas.addEventListener('touchend', () => {
if (isPainting) {
isPainting = !isPainting;
points = [];
}
}, { passive: true })
}
function addToDrawingPointsTouch(canvas, mouseEvent) {
const rect = canvas.getBoundingClientRect();
points.push({
x: (mouseEvent.touches[0].clientX - rect.left) / (rect.right - rect.left) * canvas.width,
y: (mouseEvent.touches[0].clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
});
}
function isMobile() {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
return true
} else {
return false;
}
}
function isIpad() {
if (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
return true;
}
else {
false;
}
}
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
function createCanvasPngDataUrl() {
return canvas.toDataURL('image/png');
}
body {
user-select: none;
-webkit-user-select: none;
touch-action: none;
-ms-touch-action: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
<script defer src='main.js'></script>
</head>
<body>
<form id="form">
<canvas style="border: 1px solid black;" id="sign">
</canvas>
</form>
<div id="menu">
<button onclick="createCanvasPngDataUrl()">Create DataUrl</button>
<button onclick="clearCanvas()" id="clear">Clear</button>
</div>
</body>
</html>

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

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";
}

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.

HTML5 Canvas Image doesn't move

I have a problem. I'm using HTML5 and Javascript to draw a box onto a canvas. This box is supposed to move up and down, but after it gets rendered for the first time, it doesn't want to re-render at the new position. This is the code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sl" lang="sl">
<head>
<title>Spletna stran Portfolio - Domaca stran</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" href="css/style.css" type="text/css" media="all" />
<script>
var isMoving = true;
var gradienty = 0;
var gradientheight = 100;
var gradientyVel = 1;
var gradientImage = new Image();
gradientImage.src = "grafika/gradient.gif";
function main() {
var c = document.getElementById("menuCanvas");
var ctx = c.getContext("2d");
ctx.drawImage(gradientImage, 0, gradienty);
if(isMoving == true) {
gradienty += gradientyVel;
if((gradienty+gradientheight)==c,height || gradienty<=0) {
gradientyVel *= -1;
}
}
}
function mainLoop() {
setInterval(main(), 10);
}
</script>
</head>
<body onload="mainLoop()">
<canvas id="menuCanvas" width="195" height="400"></canvas>
</body>
</html>
I have no clue why the box isn't moving. It's like after I draw it for the first time it won't draw again the next time. Also, I use mainLoop() function as my main loop. Every 10 miliseconds I basically call the main() function, which does the drawing and the logic.
Any help would be much appreciated. :)
A few coding problems:
You need to give your image time to load, then start the animation
var gradientImage = new Image();
gradientImage.onload=function(){
setInterval(main, 20);
}
gradientImage.src = "house16x16.png";
setInterval takes in a function pointer (no parentheses):
setInterval will fire only as fast as 16ms, so your 10ms value is too small
setInterval(main, 20);
Here's revised code and a Demo: http://jsfiddle.net/m1erickson/U6K9j/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var c=document.getElementById("menuCanvas");
var ctx=c.getContext("2d");
var isMoving = true;
var gradienty = 0;
var gradientheight = 100;
var gradientyVel = 1;
var gradientImage = new Image();
gradientImage.onload=function(){
setInterval(main, 20);
}
gradientImage.src = "house16x16.png";
function main() {
ctx.clearRect(0,0,c.width,c.height);
ctx.drawImage(gradientImage, 0, gradienty);
if(isMoving == true) {
gradienty += gradientyVel;
if((gradienty+gradientheight)==c.height || gradienty<=0) {
gradientyVel *= -1;
}
}
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="menuCanvas" width=300 height=300></canvas>
</body>
</html>
You're calling main and then passing the RESULT to setInterval. Just change
function mainLoop() {
setInterval(main(), 10);
}
to
function mainLoop() {
setInterval(main, 10);
}
That'll get you started, but you'll also need to clear the canvas in between draws. (As you'll notice if you run this)

Adding a background image under a background color function

I have the three functions running on my interactive pet (change background, dimmer lights, mouse follow). I'm trying to add an image to the background of the body, but every time I try it gets pushed to the top and the dirty/clean tank function doesn't work. Could someone tell me how to change the color sections of the first function below to switch between images instead of color codes? I think that would solve the immediate problem. :/
CSS Code:
//change background color: dirty/clean tank
setTimeout(function dirty() { //sets background color to murky green
document.body.style.backgroundColor = ****"#CCCC99";** //would like this to be an image**
var btn = document.body.appendChild(document.createElement('button'));
btn.appendChild(document.createTextNode("Clean the tank!"));
document.body.style.marginTop = "-1000px";
btn.onclick = function clean() {
btn.parentNode.removeChild(btn);
document.body.style.backgroundColor = **"#CCFFFF";//would like this to be an image**
setTimeout(dirty,27000); //how long it takes to make the tank dirty
};
},500); //first loading of clear blue timeout
//dim the lights
var dimmed = 0;
function toggleLights()
{
var dimmer = document.getElementById("dimmer");
if(dimmed == 0) dimmed = 1;
else dimmed = 0;
if(dimmed == 1)
{
dimmer.style.opacity = 0.8;
dimmer.style.visibility = "visible";
}
if(dimmed == 0)
{
dimmer.style.opacity = 0.0;
dimmer.style.visibility = "hidden";
}
}
//fish moves on mouse hover
$(document).ready(function() {
$("#swim").mousemove(function (event) {
var fish = $("#fish1");
var position = fish.position();
var mousey = event.pageX;
if (position.left > mousey) {
$("#fish1").html("<img src='images/happy.png' />");
} else {
$("#fish1").html("<img src='images/happyback.png'/>");
}
$("#fish1").stop().animate({
left: event.pageX,
top: event.pageY
}, 300);
});
});
HTML Code:
<HTML>
<HEAD>
<TITLE>Untitled</TITLE>
<link href="style.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"
</script>
<script type="text/javascript" src="pet.js"></script>
<script type="text/javascript" src="core.js"></script>
</HEAD>
<div id="table">
<div id="dimmer" onClick="toggleLights()"></div>
<a href="javascript:toggleLights();"><img src="images/table-lamp.png" height="380px"
alt="table lamp"></a>
</div>
<div id="swim">
<div id="fish1"><img src="images/happy.png"/></div>
</div>
</BODY>
</HTML>
Add a starting <body> tag in your HTML, and change element classes with javascript, instead of style properties. That way you have more control over the styling through your CSS file.
So you would do something like:
document.body.className = 'state1';
And in your CSS
.state1{
background-image: url(the/image.jpg);
}
and so on...

Categories