Create Scrolling Event On Canvas - javascript

I have a popup with a canvas that displays a graph, but sometimes the graph is bigger than the canvas is there a way that I can add some sort of scrolling event to allow the user to scroll horizontally, to see the rest of the graph?

Canvas is a normal DOM element like img, so you can use the regular CSS styles to define the scrolling behavior. For example:
$(document).ready(function(){
var ctx = $("#canvs").get(0).getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0,0,1600,300);
ctx.strokeStyle = "lime";
ctx.moveTo(0, 150);
for (var i = 0; i <= 16; i++){
var y = i % 2 == 0 ? 50 : 250;
ctx.lineTo(i * 100, y);
ctx.stroke();
}
});
#cont{
display: block;
width: 500px;
height: 330px;
overflow: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='cont'>
<canvas id='canvs' width='1600px' height='300px'></canvas>
</div>

Related

How to animate properly in a <canvas>?

So I want to recreate this simple animation effect, which uses a <canavs> :
https://codepen.io/w3devcampus/pen/PpLLKY
HTML:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Draw a monster in a canvas</title>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
</body>
</html>
JavaScript:
function mainLoop() {
ctx.clearRect(0, 0, w, h);
drawMyMonster(xMonster, yMonster);
xMonster += monsterSpeed;
if (((xMonster + 100)> w) || (xMonster < 0)) {
monsterSpeed = -monsterSpeed;
}
}
function drawMyMonster(x, y) {
ctx.save();
ctx.translate(x, y);
ctx.strokeRect(0, 0, 100, 100);
ctx.fillRect(20, 20, 10, 10);
ctx.fillRect(65, 20, 10, 10);
ctx.strokeRect(45, 40, 10, 40);
ctx.strokeRect(35, 84, 30, 10);
ctx.fillRect(38, 84, 10, 10);
ctx.fillRect(52, 84, 10, 10);
ctx.restore();
}
So, I did recreate it, but it wouldn't behave normally. Instead of displaying one frame at a time, it stacks all the frames up each other in the canvas. Here's my version:
https://codepen.io/Undefined_Variable/pen/LraLJQ
HTML:
<html>
<head>
<title>CanvasThingy</title>
</head>
<body>
<canvas id="CanvasForMonster">
No support for you scrub!
</canvas>
</body>
</html>
JavaScript:
var myCanvas, context, w, h;
var xMonster = 60;
var yMonster = 30;
var monsterSpeed = 1;
window.onload = function initial(){
myCanvas = document.body.querySelector('#CanvasForMonster');
w = myCanvas.style.width;
h = myCanvas.style.height;
context = myCanvas.getContext('2d');
ClearCanvas();
}
function ClearCanvas(){
context.clearRect(0, 0, w, h);
DrawFMLface(xMonster, yMonster);
xMonster += monsterSpeed;
if(((xMonster + 175) > 299) || (xMonster < 0) ){
monsterSpeed = -monsterSpeed;
}
requestAnimationFrame(ClearCanvas);
}
function DrawFMLface(x, y){
context.save();
context.translate(x, y);
context.strokeRect(0, 4, 175, 80);
context.strokeStyle = 'black';
context.fillRect(40, 10, 20, 20);
context.strokeStyle = 'black';
context.fillRect(110, 10, 20, 20);
context.strokeStyle = 'black';
context.strokeRect(85, 40, 1, 15);
context.strokeStyle = 'black';
context.strokeRect(42, 70, 90, 0);
context.strokeStyle = 'black';
context.restore();
}
Please tell me how to do this properly without jQuery AT ALL, please. I'm still learning and I don't want any jQuery/Angular/React etc. in my code, please!
Your problem is that the previous faces are not being cleared. The clearing is supposed to be done with this line:
ctx.clearRect(0, 0, w, h);
The problem is that w and h are not set correctly when this is run, so not enough of the canvas is cleared.
This is where you set w and h:
w = myCanvas.style.width;
h = myCanvas.style.height;
This will only work if the width and height were set with CSS, e.g. with an HTML attribute style="width: 200px; height: 200px;", and if you called parseInt to convert the CSS style strings into numbers. But in fact the canvas element’s width and height were set with width and height HTML attributes directly. So you should set w and h with HTMLCanvasElement’s width and height properties:
w = canvas.width;
h = canvas.height;
The original Codepen sets them this way.
Just in case you were looking for a possibly easier way to do this, you can actually avoid JavaScript entirely when creating a simple animation like this by using CSS animations:
#CanvasForMonster {
position: relative;
width: 200px;
height: 200px;
border: 1px solid #000;
}
#Monster {
position: absolute;
top: 25%;
width: 50%;
height: 50%;
animation: bounce 2s alternate linear infinite;
}
#keyframes bounce {
from { left: 0; }
to { left: 50%; }
}
#Monster div {
position: absolute;
}
#Monster .stroke {
border: 1px solid #000;
}
#Monster .fill {
background-color: #000;
}
#Monster .head {
left: 0;
top: 0;
width: 100%;
height: 100%;
}
#Monster .eye {
width: 10%;
height: 10%;
}
#Monster .eye-left {
left: 25%;
top: 25%;
}
#Monster .eye-right {
left: 65%;
top: 25%;
}
#Monster .nose {
left: 50%;
top: 50%;
width: 1px;
height: 10%;
}
#Monster .mouth {
left: 25%;
top: 75%;
width: 50%;
height: 0;
}
<div id="CanvasForMonster">
<div id="Monster">
<div class="stroke head"></div>
<div class="fill eye eye-left"></div>
<div class="fill eye eye-right"></div>
<div class="stroke nose"></div>
<div class="stroke mouth"></div>
</div>
</div>
Everything you have is alright.
The problem is your Globals.
The width of the canvas should be set to:
w = myCanvas.width;
Height set to:
h = myCanvas.height;
You have both set as:
w = myCanvas.style.width;
h = myCanvas.style.height;
Which will not work.
Here is the fork (Demo).
I would highly recommend using JQuery when working with canvas.
When using canvas it should be noted that you want too reference canvas.height, and canvas.width as much as possible rather then using a global that points at the height, and width of the css style.

How do I add a onClick event handler to a canvas element and control mouse movement actions?

I have an image within a canvas, I want to be to click the image and drag the image(without lifting the left mouse button) and the image at the position where the left mouse button was released. For now as soon as the mouse icon hovers over the canvas, the image moves with it, I tried to add an onclick listener event but I am sure my newbie-ness got in the way of my progress.
Here is what I had come up with so far. How can I make this work with my existing code?
var canvas = document.getElementById('customCanvas');
var context = canvas.getContext('2d');
make_base();
function make_base()
{
upload_image = new Image();
upload_image.src = 'https://lh3.googleusercontent.com/-6Zw-hozuEUg/VRF7LlCjcLI/AAAAAAAAAKQ/A61C3bhuGDs/w126-h126-p/eagle.jpg';
upload_image.onload = function(){
context.drawImage(upload_image, 0, 0);
canvas.addEventListener('click', canvas.onmousemove = function(e) {
/// correct mouse position so its relative to canvas
var rect = canvas.getBoundingClientRect(),
constantX = 0, constantY = 0,
x = e.clientX - rect.left,
y = e.clientY - rect.top;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(upload_image, x, y);
});
}
}
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
.sidepane {
height: 100%;
background: #E8E8EA;
}
.sidepane .form {
height: 80px;
margin: 10px 0;
}
.sidepane .assets {
width: 100%;
}
#assetText {
font-size: 24px;
}
.assets .text, .assets .image {
margin: 10px 0;
}
.assets .image ul li {
width: 50px;
height: 50px;
margin-right: 5px;
float: left;
overflow: hidden;
}
.assets .image ul li img {
width: 100%;
height: 100%;
}
.canvas .block {
position: relative;
width: 600px; height: 600px;
margin: 10px;
border: 1px solid;
box-shadow: 0px 0px 5px black;
}
.item {
border: 1px solid transparent;
position: absolute;
}
.item.selected {
border-color: blue;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="sidepane col-sm-2 col-md-2 col-lg-2">
<form method="post" action="/images" enctype="multipart/form-data">
<!--<div class="form">-->
<h3>Form</h3>
<input type="file" class="form-control" placeholder="Upload Your Images" name="filefield">
<button id="submit" class="btn btn-default">upload</button>
<!-- Upload Form here -->
<!--</div>-->
<hr />
<div class="assets">
<h3>Assets</h3>
<div class="text">
<h4>Text</h4>
<input type="text" name="textfield">
<button id="addText" class="btn btn-default">Add Text</button>
</div>
<div class="image">
<h4>Images</h4>
<ul class="list-unstyled">
<!-- List of images here -->
<!-- <li><img src="images/sample.jpeg" class="img-rounded" /></li> -->
</ul>
</div>
</div>
<input type="submit" >
</form>
</div>
<!-- canvas -->
<div class="canvas col-sm-8 col-md-8 col-lg-8">
<div class="block">
<!-- Add images and texts to here -->
<canvas id="customCanvas" width="598" height="598" style="border: 1px solid #000000">
</canvas>
</div>
</div>
First, you have to check if your mouse is on the image, and then check if you are trying to drag the image. To do that, you need some events, mousedown, mouseup and mousemove. To check if your mouse pointer is on the image, you have to get the X, Y, width, height of that image. Final code below.
Edit
Some more changes. Image class has no X and Y properties so I had to define variables that will store that data and make some changes to isInside function.
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 300;
var upload_image;
var imageX, imageY;
var mouseX, mouseY;
var imageDrag = false;
make_base();
canvas.addEventListener("mousemove", function (evt) {
var mousePos = getMousePos(canvas, evt);
mouseX = mousePos.x;
mouseY = mousePos.y;
});
function getMousePos(canvas, event) {
var rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
function isInsideImage(rect) {
var pos = { x: mouseX, y: mouseY };
return pos.x > imageX && pos.x < imageX + rect.width && pos.y < imageY + rect.height && pos.y > imageY;
}
function make_base()
{
upload_image = new Image();
imageX = 0;
imageY = 0;
upload_image.onload = function(){
context.drawImage(upload_image, 0, 0);
}
upload_image.src = 'https://lh3.googleusercontent.com/-6Zw-hozuEUg/VRF7LlCjcLI/AAAAAAAAAKQ/A61C3bhuGDs/w126-h126-p/eagle.jpg';
}
canvas.addEventListener("mousedown", function (evt) {
if(isInsideImage(upload_image)) {
imageDrag = true;
}
});
canvas.addEventListener("mouseup", function (evt) {
if(imageDrag)
imageDrag = false;
});
setInterval(function() {
if(imageDrag) {
context.clearRect(0, 0, canvas.width, canvas.height);
imageX = mouseX;
imageY = mouseY;
context.drawImage(upload_image, imageX, imageY);
}
}, 1000/30);
The event you're looking for would be https://developer.mozilla.org/en/docs/Web/Events/mousedown - AFAIK (correct me if I'm wrong) but the click event would only fire when a complete click event has completed (both down and up).
Here's some sample code for this;
var mouseX;
var mouseY; // Accessible outside the function. Easier access to canvas drawing.
var canvas = ''; // Complete this to get canvas element
canvas.addEventListener("mousedown", function(mouse){
// Get mouse co-ordinates
})
Inside this event listener, you can check for your current mouse position...
var canvasElement = element.getBoundingClientRect()
mouseX = mouse.pageX - canvasElement.left;
mouseY = mouse.pageY - canvasElement.top;
Use these variables when drawing your image to the canvas to determine the image's x and y position. These should change as your mouse moves around the canvas. I.e, pass them to your make_base() function;
make_base(mouseX, mouseY)
Update your drawing function to account for them;
function make_base(mouseX, mouseY)
{
upload_image = new Image();
upload_image.src = 'https://lh3.googleusercontent.com/-6Zw-hozuEUg/VRF7LlCjcLI/AAAAAAAAAKQ/A61C3bhuGDs/w126-h126-p/eagle.jpg';
upload_image.onload = function(){
context.drawImage(upload_image, 0, 0);
canvas.addEventListener('click', canvas.onmousemove = function(e) {
/// correct mouse position so its relative to canvas
var rect = canvas.getBoundingClientRect(),
constantX = 0, constantY = 0,
x = mouseX,
y = mouseY
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(upload_image, x, y);
});
}
}
PLEASE NOTE that the code above is not complete, for example, the X and Y will be based on where your mouse is on the PAGE, not the CANVAS. there are separate calculations needed to account for this.
Or, you can just embed an external link under the image to take you to wherever you want!

getting canvas pixel data is wrong after setting it with css

i am trying to get the rgb color data from a painted canvas.
with this function:
function pick(event) {
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
$("#color").val(pixel.data.toString());
console.log(pixel.data.toString());
}
however, when i am setting the canvas element to any relative width (80%/100%), like this:
canvas {
/*width: 100%;*/ /*when i set the width, the rgb values are incorrect .... */
/*margin-left: auto;
margin-right: auto;
display: block;*/
}
i am no longer getting correct values from this function.
attaching my JS Fiddle:
https://jsfiddle.net/iliran11/ay9nf1p9/
You need to find the difference in width when you stretch and multiply that to the coordinates.
//color names
var basecolors = ["(255,0,0)",
"(255,128,0)",
"(255,255,0)",
"(128,255,0)",
"(0,255,0)",
"(0,255,128)",
"(0,255,255)",
"(0,128,255)",
"(0,0,255)",
"(128,0,255)",
"(255,0,255)",
"(255,0,128)",
"(128,128,128)"
];
//init
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var heightPerColor = height / basecolors.length;
//init cursor
var xCursor = 0;
var yCursor = 0;
drawPallete(width, height, "s");
function drawPallete(width, height, type) {
canvas.width = width;
canvas.height = height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
var Grad = ctx.createLinearGradient(0, 0, canvas.width, 0);
Grad.addColorStop(0 / 6, '#F00');
Grad.addColorStop(1 / 6, '#FF0');
Grad.addColorStop(2 / 6, '#0F0');
Grad.addColorStop(3 / 6, '#0FF');
Grad.addColorStop(4 / 6, '#00F');
Grad.addColorStop(5 / 6, '#F0F');
Grad.addColorStop(6 / 6, '#F00');
ctx.fillStyle = Grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
;
function pick(event) {
var canvas = document.getElementById("canvas");
//Here comes the stretch offset
//var off = (1 / canvas.width) * canvas.offsetWidth;
var off = (1 / canvas.offsetWidth) * canvas.width;
//Applying offset
var x = Math.floor(event.layerX * off) - Math.floor(canvas.offsetLeft * off);
var y = Math.floor(event.layerY * off) - Math.floor(canvas.offsetTop * off);
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
$("#color").val([pixel.data[0], pixel.data[1], pixel.data[2], pixel.data[3]].join(','));
//console.log("offset between", canvas.width, "and", canvas.offsetWidth, "is", off);
console.log(off, canvas.offsetWidth, canvas.width, x);
}
// target the button
var btn = $("#myBtn");
var modal = $("#myModal");
btn.click(function () {
modal.css("display", "block");
var canvas = document.getElementById("canvas");
canvas.addEventListener('mousemove', pick);
});
#myModal {
display: none;
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
padding-top: 100px;
/* Location of the box */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
canvas {
width: 100%;
/*margin-left: auto;
margin-right: auto;
display: block;*/
}
<form action="" method="post">
<label for="POST-name">Name:</label>
<input id="color" type="text">
</form>
<button id="myBtn">Open Color</button>
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<!-- Set height and width -->
<canvas id="canvas" width="1000" height="1000"></canvas>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.js" integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA=" crossorigin="anonymous"></script>
EDIT 1
Compensated for offsetLeft and offsetTop.

How can an element of a fixed size (canvas) be resized in a flex container?

An HTML canvas element can be automatically resized by the dynamics of a HTML page: Maybe because its style properties are set to a percentage value, or maybe because it is inside a flex container.
Thereby the content of the canvas gets resized as well: Because of the interpolation the canvas content looks blurred (loss of resolution).
In order to keep the resolution to the maximum, one must render the canvas content based on the current pixel size of the element.
This fiddle shows how this can be done with the help of the function getComputedStyle: https://jsfiddle.net/fx98gmu2/1/
setInterval(function() {
var computed = getComputedStyle(canvas);
var w = parseInt(computed.getPropertyValue("width"), 10);
var h = parseInt(computed.getPropertyValue("height"), 10);
canvas.width = w;
canvas.height = h;
// re-rendering of canvas content...
}, 2000); // intentionally long to see the effect
With a setTimeout function I'm updating the width and height of the canvas to the computed values of these properties.
As the fiddle shows, this works only when increasing the size of the window. Whenever the window size gets decreased, the canvas (item of the flexbox) stays fixed.
Set its flex-basis to 0, allow it to shrink with flex-shrink, and don't force any minimum width:
#canvas {
flex: 1 1 0;
min-width: 0;
}
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
setInterval(function() {
var computed = getComputedStyle(canvas),
w = parseInt(computed.width, 10),
h = parseInt(computed.height, 10);
canvas.width = w;
canvas.height = h;
ctx.clearRect(0, 0, w, h);
ctx.beginPath();
ctx.arc(w/2, h/2, h/2, 0, Math.PI*2, true);
ctx.stroke();
}, 2000); // intentionally long to see the effect
#container {
border: 1px solid blue;
width: 100%;
display: flex;
}
#canvas {
border: 1px solid red;
flex: 1 1 0;
min-width: 0;
height: 150px;
}
<div id="container">
<canvas id="canvas"></canvas>
<div>something else...</div>
</div>
You need to place the canvas into a container with overflow:hidden and set canvas dimensions to dimensions of that container:
window.setTimeout( function() {
var div = $("div");
div.find("canvas").attr( { width:div.width(), height:div.height()} );
})
canvas { background:gold; display:block; }
div { width:50%; height:50%; border:1px solid; overflow:hidden; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<canvas width="200" height="100" />
</div>

Drawing in canvas become offset

I'm trying to draw into a canvas, but the more I go right, the more offset my drawing become.
Anyone have an idea why?
I have included the relevant code below:
CSS
html,body {
width:100%;
height:100%;
background:rgba(0,0,0,.2);
}
#container {
position:relative;
width:700px;
height:450px;
background:#fff;
overflow:hidden;
}
* {
-webkit-user-select: none;
}
canvas {
position: absolute;
top: 0;
left: 0;
background: #ccc;
width: 500px;
height: 200px;
}
HTML
<div id='adContainer'>
<canvas></canvas>
</div>
Javascript
var ctx;
var can = $('canvas');
$(document).ready(function() {
ctx = can[0].getContext('2d');
ctx.strokeStyle = "rgba(255,0,0,1)";
ctx.lineWidth = 5;
ctx.lineCap = 'round';
can.on("touchstart", function(event) {
event.preventDefault();
var e = event.originalEvent;
if(e.touches.length == 1) {
var posX = e.touches[0].pageX;
var posY = e.touches[0].pageY;
ctx.moveTo(posX, posY);
}
});
can.on("touchmove", function(event) {
event.preventDefault();
var e = event.originalEvent;
if(e.touches.length == 1) {
var posX = e.touches[0].pageX;
var posY = e.touches[0].pageY;
ctx.lineTo(posX, posY);
ctx.stroke();
}
});
});
Demo: http://jsfiddle.net/8Wtf8/
That is because you define the size of the canvas using CSS.
What happens is that when you don't explicitly define the size of the canvas using its width and height attributes the canvas defaults to size 300 x 150.
In your CSS you are then stretching the canvas element (look at it as an image) to 500px etc. - the content of the canvas is still 300 x 150.
You need to set the width and height on the canvas element itself:
<canvas width=500 height=200 id="myCanvas"></canvas>
and remove the definition from the CSS:
canvas {
position: absolute;
top: 0;
left: 0;
background: #ccc;
/*width: 500px;
height: 200px;*/
}
Also notice that background set by CSS will not be part of the canvas content.

Categories