P5js having a div element behind a canvas element - javascript

I added a gif as an element and now I want to lay it behind a hollowed out brain image I loaded on my canvas. The gif element lays on top of the brain image and I want to lay it behind the brain image. Is there a way to do this? Here is the link to the thimble https://thimbleprojects.org/ejonescc16/63728/.
var brains;
var coolpup;
var canvas;
function preload(){
brains = loadImage('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/kevin%20final/assets/brains.png');
coolpup = createImg('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/Brain/gifs/pup.gif');
}
function setup() {
createCanvas(windowWidth,windowHeight);
strokeWeight(2);
rSlider = createSlider(0, 255, 100);
rSlider.position(50, windowHeight-80);
gSlider = createSlider(0, 255, 0);
gSlider.position(50, windowHeight-60);
bSlider = createSlider(0, 255, 255);
bSlider.position(50, windowHeight-40);
}
function draw() {
background(0);
r = rSlider.value();
g = gSlider.value();
b = bSlider.value();
imageMode(CENTER);
coolpup.position(windowWidth/2-350, windowHeight/2-150);
coolpup.size(130,200);
image(brains, windowWidth/2, windowHeight/2);//DISPLAYS BRAINS
fill(255);
textSize(30);
text("This is my brain", windowWidth/2-375, windowHeight-100);
text("This is my brain while coding", windowWidth/2+65, windowHeight-100);
}

I noticed you used createImg to load your gif image.
You can instead use this library to display gifs.
https://github.com/antiboredom/p5.gif.js
This said, you have to first draw the gif the add the mask on top.
var brains, coolpup, coding;
var canvas;
function preload() {
brains = loadImage('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/kevin%20final/assets/brains.png');
coolpup = loadGif('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/Brain/gifs/pup.gif');
coding = loadGif('https://media.giphy.com/media/11Yfb7wNfpIEfK/giphy.gif');
}
function setup() {
createCanvas(windowWidth, windowHeight);
strokeWeight(2);
imageMode(CENTER);
}
function draw() {
background(0);
if (mouseX < width * 0.5) {
image(coolpup, mouseX, mouseY);
} else {
image(coding, mouseX, mouseY);
}
image(brains, windowWidth / 2, windowHeight / 2); //DISPLAYS BRAINS
}
My example displays the image attached to the center of the mouse and draws either one or the other image depending on which half of the canvas you hover.

Related

p5.js : Trouble with clicking buttons and objects

I am really new to p5.js, and I'm trying to find a solution to this for a few days with no luck.
I have a button and an ellipse. When the button is clicked a rectangle appears on the canvas and when the ellipse is clicked its color changes.The problem is that I can't have both at the same time and the reason is this part of the code:
stroke(rgb);
strokeWeight(2);
If I have it on my code, when I click the button the rectangle appears, but when I click the ellipse nothing happens. If I leave it out of my code, I can change the ellipse's color, but when I click the button nothing happens.
I have no idea why these two lines of code make it impossible to interact with both the button and the shape, and I'd really like to find out. Am I missing something important?
My code is the following:
function setup(){
createCanvas(800, 600);
bubble = new new_ellipse(500,300);
}
function draw() {
background(51);
button = createButton("clickme");
button.position(100, 65);
button.mousePressed(show_rect);
bubble.display();
stroke(rgb);
strokeWeight(2);
}
function mousePressed(){
bubble.clicked();
}
function new_ellipse(x,y){
this.x = x;
this.y = y;
this.col = color(255,100);
this.display = function(){
stroke(255);
fill(this.col);
ellipse(this.x, this.y, 100, 100);
}
this.clicked = function(){
var d = dist(mouseX, mouseY, this.x, this.y);
if(d < 50){
this.col = color(233,11,75);
}
}
}
function show_rect(){
fill(255,24,23);
rect(200,200,100,100);
}
Two related issues:
Don't call createButton inside draw. draw is called about 60 times a second, so this spams buttons. draw is your animation loop. Use setup to set things up.
Given that draw() runs 60 times a second, any calls to show_rect triggered by a button press will be almost instantly wiped clean by background(51) a split second later.
One option is to introduce a boolean to keep track of whether the button has been pressed yet, then re-draw it every frame if it's supposed to be visible.
Another approach is to drop the draw() function and re-paint only when state changes. This prevents background(51) from blasting everything and is efficient, but also isn't suitable if you have animations or a good deal of dynamism in your app.
Here's a sample of the first approach, using a boolean:
var bubble, button;
var shouldShowRect = false;
function setup() {
createCanvas(800, 600);
bubble = new Ellipse(500, 300);
button = createButton("clickme");
button.mousePressed(function () {
// or set to true if you don't want to toggle
shouldShowRect = !shouldShowRect;
});
button.position(100, 65);
}
function draw() {
background(51);
bubble.display();
if (shouldShowRect) {
showRect();
}
strokeWeight(2);
}
function mousePressed() {
bubble.clicked();
}
function Ellipse(x, y) {
this.x = x;
this.y = y;
this.col = color(255, 100);
this.display = function() {
stroke(255);
fill(this.col);
ellipse(this.x, this.y, 100, 100);
}
this.clicked = function() {
var d = dist(mouseX, mouseY, this.x, this.y);
if (d < 50) {
this.col = color(233, 11, 75);
}
}
}
function showRect() {
fill(255, 24, 23);
rect(200, 200, 100, 100);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

p5.js: Why is my ellipse flashing?

I have an ellipse that scales through draw(), but for some reason, it flashes uncontrollably. I can't seem to figure out why. I suspect it has to do with setTimeout. I need it because I need to wait 10 seconds before drawing the ellipse Here's the code:
//diameter of ellipse that increments
var dia1 = 0;
var dia2 = 0;
function setup() {
createCanvas(400,400);
stroke(255);
noFill();
frameRate(40);
}
//draw and increment ellipse
function circle1() {
ellipse(width/2,height/2, dia1,dia1);
dia1 = dia1+1;
if (dia1 >= width) {
dia1 = 0;
}
}
function circle2() {
ellipse(width/2,height/2, dia2,dia2);
dia2 = dia2+1;
if (dia2 >= width) {
dia2 = 0;
}
}
function draw() {
background(40,40,40);
//wait 10 seconds before drawing ellipse
setTimeout(function() { circle1(); }, 10000);
circle2();
console.log(dia1);
}
You should not use setTimeout() to call drawing functions.
If you want to do timing, use the millis() function. More info is available in the reference, but a basic program would look like this:
function draw(){
background(0);
if(millis() > 10000){
ellipse(width/2, height/2, 25, 25);
}
}

Image in canvas leaves a tiled trail when panned

I am trying to create a pannable image viewer which also allows magnification. If the zoom factor or the image size is such that the image no longer paints over the entire canvas then I wish to have the area of the canvas which does not contain the image painted with a specified background color.
My current implementation allows for zooming and panning but with the unwanted effect that the image leaves a tiled trail after it during a pan operation (much like the cards in windows Solitaire when you win a game). How do I clean up my canvas such that the image does not leave a trail and my background rectangle properly renders in my canvas?
To recreate the unwanted effect set magnification to some level at which you see the dark gray background show and then pan the image with the mouse (mouse down and drag).
Code snippet added below and Plnkr link for those who wish to muck about there.
http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1
<!DOCTYPE html>
<html>
<head>
<style>
canvas{
border:solid 5px #333;
}
</style>
</head>
<body>
<button onclick="changeScale(0.10)">+</button>
<button onclick="changeScale(-0.10)">-</button>
<div id="container">
<canvas width="700" height="500" id ="canvas1"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas1');
var context = canvas.getContext("2d");
var imageDimensions ={width:0,height:0};
var photo = new Image();
var isDown = false;
var startCoords = [];
var last = [0, 0];
var windowWidth = canvas.width;
var windowHeight = canvas.height;
var scale=1;
photo.addEventListener('load', eventPhotoLoaded , false);
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function eventPhotoLoaded(e) {
imageDimensions.width = photo.width;
imageDimensions.height = photo.height;
drawScreen();
}
function changeScale(delta){
scale += delta;
drawScreen();
}
function drawScreen(){
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
canvas.onmousedown = function(e) {
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e) {
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
};
canvas.onmousemove = function(e)
{
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]);
drawScreen();
}
</script>
</body>
</html>
You need to reset the transform.
Add context.setTransform(1,0,0,1,0,0); just before you clear the canvas and that will fix your problem. It sets the current transform to the default value. Then befor the image is draw set the transform for the image.
UPDATE:
When interacting with user input such as mouse or touch events it should be handled independently of rendering. The rendering will fire only once per frame and make visual changes for any mouse changes that happened during the previous refresh interval. No rendering is done if not needed.
Dont use save and restore if you don't need to.
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext("2d");
var photo = new Image();
var mouse = {}
mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0;
mouse.down = false;
var changed = true;
var scale = 1;
var imageX = 0;
var imageY = 0;
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function changeScale(delta){
scale += delta;
changed = true;
}
// Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers.
function mouseEvents(event){ // do it all in one function
if(event.type === "mouseup" || event.type === "mouseout"){
mouse.down = false;
changed = true;
}else
if(event.type === "mousedown"){
mouse.down = true;
}
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.down) {
changed = true;
}
}
canvas.addEventListener("mousemove",mouseEvents);
canvas.addEventListener("mouseup",mouseEvents);
canvas.addEventListener("mouseout",mouseEvents);
canvas.addEventListener("mousedown",mouseEvents);
function update(){
requestAnimationFrame(update);
if(photo.complete && changed){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle="#333";
ctx.fillRect(0,0, canvas.width, canvas.height);
if(mouse.down){
imageX += mouse.x - mouse.lastX;
imageY += mouse.y - mouse.lastY;
}
ctx.setTransform(scale, 0, 0, scale, imageX,imageY);
ctx.drawImage(photo,0,0);
changed = false;
}
mouse.lastX = mouse.x
mouse.lastY = mouse.y
}
requestAnimationFrame(update);
canvas{
border:solid 5px #333;
}
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button>
<canvas width="700" height="500" id ="canvas1"></canvas>
Nice Code ;)
You are seeing the 'tiled' effect in your demonstration because you are painting the scaled image to the canvas on top of itself each time the drawScreen() function is called while dragging. You can rectify this in two simple steps.
First, you need to clear the canvas between calls to drawScreen() and second, you need to use the canvas context.save() and context.restore() methods to cleanly reset the canvas transform matrix between calls to drawScreen().
Given your code as is stands:
Create a function to clear the canvas. e.g.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
In the canavs.onmousemove() function, call clearCanvas() and invoke context.save() before redefining the transform matrix...
canvas.onmousemove = function(e) {
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
/* !!! */
clearCanvas();
context.save();
context.setTransform(
1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]
);
drawScreen();
}
... then conditionally invoke context.restore() at the end of drawScreen() ...
function drawScreen() {
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
/* !!! */
if (isDown) context.restore();
}
Additionally, you may want to call clearCanvas() before rescaling the image, and the canvas background could be styled with CSS rather than .fillRect() (in drawScreen()) - which could give a performance gain on low spec devices.
Edited in light of comments from Blindman67 below
See Also
Canvas.context.save : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
requestAnimationFrame : https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Paul Irish, requestAnimationFrame polyfill : http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Call context.save to save the transformation matrix before you call context.fillRect.
Then whenever you need to draw your image, call context.restore to restore the matrix.
For example:
function drawScreen(){
context.save();
context.fillStyle="#333333";
context.fillRect(0,0, windowWidth, windowHeight);
context.restore();
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
Also, to further optimize, you only need to set fillStyle once until you change the size of canvas.

Background image translate?

I'm attempting to get basic side scroller movement down in canvas, and I'm in a good spot with the movement itself, but I can't seem to get the background to translate. Perhaps I'm misunderstanding how translate works? The main canvas is translating fine (the one the 'player' is on) but the bg canvas won't budge.
http://jsfiddle.net/Y5SG8/1/
Fullscreen: http://jsfiddle.net/Y5SG8/1/embedded/result/
Any help would be greatly appreciated.
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
var canvas = document.getElementById('canvas'),
bg = document.getElementById('canvas2'),
ctx = canvas.getContext('2d'),
bgctx = bg.getContext('2d'),
width = 1280,
height = 720,
player = {
x: width/2,
y: height/2 - 15,
width: 16,
height: 24,
speed: 3,
velx: 0,
vely: 0,
jumping: false
},
keys = [],
friction = 0.9,
gravity = 0.3;
canvas.addEventListener('keydown', function(e) {keys[e.keyCode] = true;})
canvas.addEventListener('keyup', function(e) {keys[e.keyCode] = false;})
canvas.width = width;
canvas.height = height;
bg.width = width;
bg.height = height;
var bgimg = new Image();
bgimg.src = 'bg.png';
bgimg.onload = function bgload() {bgctx.drawImage(bgimg,0,0);}
function playerupdate() {
if (keys[68]) {
if (player.velx < player.speed) {player.velx++;}
}
if (keys[65]) {
if (player.velx > -player.speed) {player.velx--;}
}
player.velx *= friction;
player.x += player.velx;
ctx.translate(-player.velx,0);
bgctx.translate(player.velx,0);
ctx.clearRect(player.x, player.y, player.width, player.height);
ctx.fillStyle = '#FF0000'
ctx.fillRect(player.x, player.y, player.width, player.height);
requestAnimationFrame(playerupdate);
console.log(player.x)
}
window.onload = playerupdate();
Short answer, the translate function translates the context of the canvas, but it does not redraw, so you'd need to:
// note you probably want the ctx to translate in the opposite direction of
// the player's velocity if you want the appearance of movement (if that's
// what you want)
bgctx.translate(-player.velx, 0);
bgctx.clearRect(0, 0, bg.width, bg.height);
bgctx.drawImage(bgimg, 0, 0);
Knowing that, you can probably figure it out from there. If your background is non-repeating (and you prevent the player from moving off the edges), then this might be the solution.
If your background is repeatable, you'll need to do a bit more work, as translating the image will quickly move it off screen. You can solve this by drawing a repeating fill created from the image rather than drawing in the image itself, something like:
// on image load, replacing your initial `drawImage`
bgimg.onload = function bgload() {
var ptrn = bgctx.createPattern(bgimg, 'repeat');
bgctx.fillStyle = ptrn;
bgctx.fillRect(0, 0, bg.width, bg.height);
}
// then in the loop
bgctx.translate(-player.velx, 0);
// Here you'd fill a square *around* the player, instead of just
// repainting the image.
bgctx.fillRect(player.x - width/2, 0, bg.width, bg.height);
As #numbers1311407 says in his answer you will need to redraw the image.
But translate is strictly not necessary here - just redraw the image into a new position instead.
bgctx.drawImage(bgimg, -player.velx, 0);
Modified fiddle
You don't even need to use clear as the image will overdraw anything - the only thing you need to take care of is wrapping/tiling when the image is out of "bound" or you will get tearing (that applies to both approaches).

scale mouse coordinates on document relative to HTML5 canvas

I have a HTML5 canvas of a certain size on the page
<canvas id="canvas" width="200" height="100" style="border:1px solid #000000;">
Right now, the canvas is painted when the mouse is dragged over it (i.e. you click first(not necessarily inside the canvas) then start dragging the mouse over it without releasing) Script below;
$(function () {
var c = document.getElementById("canvas");
var context = c.getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var $canvas=$("#canvas");
$(this).mousedown(function (e) {
paint = true;
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop);
redraw();
});
$(this).mousemove(function (e) {
if (paint) {
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop, true);
redraw();
}
});
$(this).mouseup(function (e) {
paint = false;
});
$(this).mouseleave(function (e) {
paint = false;
});
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "#000000";
context.lineJoin = "round";
context.lineWidth = 2;
for (var i = 0; i < clickX.length; i++) {
context.beginPath();
if (clickDrag[i] && i) {
context.moveTo(clickX[i - 1], clickY[i - 1]);
} else {
context.moveTo(clickX[i] - 1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
}
});
What I need now is a way to scale all the mouse coordinates on document during the document's mouse events to the relative coordinates inside the canvas.
so that no matter wherever you drag the mouse on the document it is drawn inside the canvas (in relatively small size of course). Any Idea how to achieve this?
http://jsfiddle.net/umwc5/3/
Why I need this?
It is for a signature application, When a user scribbles the signature using a tablet on a page(without seeing the page!) the entire signature is to be registered in a small canvas.
Update
The final working fiddle
The most important thing you were missing here was to multiply by the canvas/screen ratio.
First calculate the ratio:
var docToCanv = Math.min($canvas[0].width / $('body').width(), $canvas[0].height/$('body').height());
Then use it like this:
addClick(e.pageX*docToCanv, e.pageY*docToCanv);
Depending on the additional behavior you want, you may need to adjust the location a bit, but this should get you past the current issue you are having.
Demo

Categories

Resources