How to pan in PaperJS without using PaperScript - javascript

Referring to
How to pan using paperjs
I was trying to implement the pan.
This however only works for me when I use PaperScript and stops to work when I do it in regular JavaScript.
var canvas = document.querySelector('#myCanvas');
paper.install(window);
window.onload = function() {
paper.setup(canvas);
var myCircle = new Path.Circle(new Point(100, 70), 50);
myCircle.fillColor = 'black';
var toolPan = new paper.Tool();
toolPan.onMouseDrag = function (event) {
var offset = event.downPoint - event.point;
// console.log(offset);
paper.view.center = paper.view.center + offset;
};
view.draw();
}
html,
body {
margin: 0;
overflow: hidden;
height: 100%;
}
canvas[resize] {
width: 100%;
height: 100%;
}
<canvas id="myCanvas" resize></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.11/paper-full.min.js"></script>
Logging out offset to the console tells me it can’t substract the points. In PaperScript the exact same works well, however I have to make it work in regular JavaScript for my Project.

In PaperScript, you can use mathematical operators on Point instances but you can't do this in JavaScript so you have to use the corresponding methods instead.
point1 + point2 => point1.add(point2), etc...
Here is the corrected code:
var canvas = document.querySelector('#myCanvas');
paper.install(window);
window.onload = function() {
paper.setup(canvas);
var myCircle = new Path.Circle(new Point(100, 70), 50);
myCircle.fillColor = 'black';
var toolPan = new paper.Tool();
toolPan.onMouseDrag = function (event) {
var offset = event.downPoint.subtract(event.point);
// console.log(offset);
paper.view.center = paper.view.center.add(offset);
};
view.draw();
}
html,
body {
margin: 0;
overflow: hidden;
height: 100%;
}
canvas[resize] {
width: 100%;
height: 100%;
}
<canvas id="myCanvas" resize></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.11/paper-full.min.js"></script>

Related

How to display another canvas without zoom in fabricjs

In short, I am developing a project in which there are two canvases: a canvas that supports zoom and drawing, a second canvas - a duplicate of the first canvas, which should display a general view without zoom. Unfortunately, I do not understand how to implement this, since the second canvas also displays a picture with a zoom. Please, help!
Here is a small example of what I have:
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx1 = c1.getContext("2d");
var ctx2 = c2.getContext("2d");
function copy() {
var imgData = ctx1.getImageData(0, 0, 512, 512);
ctx2.putImageData(imgData, 0, 0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(() => {
copy();
}, 10)
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>
There is not a perfect solution because fabricJS does not support rendering on more targets.
There is a toCanvasElement method here that creates a copy to another canvas, but it does not let you specify the canvas you want to draw on.
So here what i m doing is, at some point:
reset the zoom to 1
render to a new canvas
put back the zoom to what it was
copy that canvas on the static canvas
There is an extra copy, that is fast on modern hardware, to the point that you do not care, but would be nicer to add a canvas argument to that toCanvasElement method in order to get the drawing happening on the specified canvas immediately
Then apart from that there is the quesiton on when firing that copy.
'after:render' is not a great idea, it will loop forever and it will also redraw on zoom changes, that is not what you want.
For now i use object:added, you can add also object:modified, and maybe something else but ideally you will call a copy manually when needed when you run some code that will change some of the drawing properties.
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx2 = c2.getContext("2d");
function copy(copiedCanvas) {
ctx2.drawImage(copiedCanvas,0,0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
function afterRender() {
var originalVP = canvas.viewportTransform;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
copy(canvas.toCanvasElement());
canvas.viewportTransform = originalVP;
}
canvas.on('object:added', afterRender);
canvas.on('object:modified', afterRender);
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>
Your issue is that you are getting the "ImageData" from the context
but you should be using the fabric.Canvas object...
The context only gives you what you see, so those two canvases will be a mirror image, you have to get the data from the fabric.Canvas that is the one that has the "big picture" with all the edits.
In my prototype I'm using canvas.toJSON to get the data then to draw that data I use loadFromJSON, but I did notice some flickering the new image is loaded so I decided to use an image instead of the static canvas, that way the transition is seamless
see my simple prototype below:
var c1 = document.getElementById("scale");
var static = document.getElementById("img");
var canvas = new fabric.Canvas(c1, {isDrawingMode: true});
function copy() {
var data = canvas.toObject();
var c = new fabric.Canvas();
c.loadFromJSON(data, function() {
static.src = c.toDataURL({ format: 'png' });
});
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
}, { crossOrigin: "anonymous" }
);
canvas.on('mouse:wheel', function(opt) {
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom() + opt.e.deltaY / 200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({
x: opt.e.offsetX,
y: opt.e.offsetY
}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(copy, 200)
canvas {
border: 2px solid black;
}
img {
border: 2px solid red;
position: absolute;
top: 5px; right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js">
</script>
<canvas id="scale" width="300" height="150"></canvas>
<br><img id="img" width="300" height="150">
Unless we get some browser compatibility issue (I only tested chrome), the result should look like:

Drawing lines using RequestAnimationFrame()

I want to draw a horizontal line when the game launches and was able to come up with something that drew the line using requestAnimationFrame() and ctx.stroke().
The problem is that when I attempt to edit the length of the line by changing the values of the end point on the line, the line remains the same length.
If anyone could take a look at it and explain what is going on I would really appreciate it.
var canvasTop = document.getElementById('top');
var ctxTop = canvasTop.getContext('2d');
var frameCountTop = 0;
var fpsTop, fpsIntervalTop, startTimeTop, nowTop, thenTop, elapsedTop;
function startAnimatingTop(fpsTop) {
fpsIntervalTop = 1000 / fpsTop;
thenTop = Date.now();
startTimeTop = thenTop;
drawTop();
}
var topLinePointA = [32, 80];
var topLinePointB = [280, 80];
var topLineStart = topLinePointA[0];
var topLineIncrement = topLineStart + 1;
var topLineEnd = topLinePointB[0];
function drawTop() {
var i = 32;
while (i < topLineEnd) {
requestAnimationFrame(drawTop);
i++;
nowTop = Date.now();
elapsedTop = nowTop - thenTop;
if (elapsedTop > fpsIntervalTop && i < topLineEnd) {
thenTop = nowTop - (elapsedTop % fpsIntervalTop);
ctxTop.lineWidth = 20;
ctxTop.strokeStyle = "black";
ctxTop.moveTo(topLineStart, 80);
ctxTop.lineTo(topLineIncrement, 80);
ctxTop.stroke();
topLineStart += 1;
} else {
cancelAnimationFrame(drawTop);
return;
}
}
}
startAnimatingTop(100);
/*HANGMAN STYLES*/
/*CLASS SELECTORS*/
.lineContainer {
/*border-style: solid;
border-color: blue;*/
}
#top {
position: absolute;
height: 5%;
width: 45%;
left: 30%;
}
#side {
position: absolute;
bottom: 20%;
left: 70%;
height: 78%;
width: 5%;
}
#bottom {
position: absolute;
bottom: 35%;
height: 5%;
width: 56%;
left: 20%;
}
#hangman {
position: absolute;
left: 30%;
height: 40%;
width: 10%;
}
<canvas id='top' class='lineContainer'></canvas>
<canvas id='side' class='lineContainer'></canvas>
<canvas id='bottom' class='lineContainer'></canvas>
<canvas id='hangman' class='lineContainer'></canvas>
<canvas id='puzzle'></canvas>
<div id='scorecard'>
</div>
There are many problems with your code.
You need to read up on using the canvas. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D will help.
Only call requestAnimationFrame once per frame. Putting it inside a while loop will just start 1000's of frames fighting for display refresh.
Start paths with ctx.beginPath();
Clear the canvas using ctx.clearRect(0,0,width,height)
Set canvas size via attributes not via style properties.
The first argument to the function called by requestAnimationFrame is the time. eg drawTop(time)
See comments for more info.
ctx.canvas.width = 100; // << set the canvas size via canvas attributes not style
ctx.canvas.height = 100;
var thenTop = performance.now(); // to get time.
requestAnimationFrame(drawTop); // <<< start the render loop with request, dont call direct
function drawTop(nowTop) { // << time passed to render call
requestAnimationFrame(drawTop); // <<< put call here not in loop
ctxTop.clearRect(0, 0, ctxTop.canvas.width, ctxTop.canvas.height); // <<< clear the canvas
var i = 32;
while (i < topLineEnd) {
// requestAnimationFrame(drawTop); // <<<<<<< Not here
i++;
elapsedTop = nowTop - thenTop;
if (elapsedTop > fpsIntervalTop && i < topLineEnd) {
thenTop = nowTop - (elapsedTop % fpsIntervalTop);
ctxTop.lineWidth = 20;
ctxTop.strokeStyle = "black";
ctxTop.beginPath(); // <<<<<< To start a new path
ctxTop.moveTo(topLineStart, 80);
ctxTop.lineTo(topLineIncrement, 80);
ctxTop.stroke();
topLineStart += 1;
} else {
return;
}
}
}

Unable to add new canvas element with an existing one

I am using 2 canvas elements. I am using one element as a main canvas element which has all the elements. I am generating a barcode in a separate canvas and would like to add it to the main canvas. Trying for a long time. Adding an example here fiddle
$(document).ready(function() {
JsBarcode('#barcode', "1234", {
width: 4,
height: 20//displayValue: false
});
var bc = document.getElementById("barcode");
var canvasx = bc.getContext("2d");
var img1 = new Image();
img1.src = bc.toDataURL("image/png");
var c = document.getElementById("labelDesigner");
var canvas = bc.getContext("2d");
c.drawImage(img1);
});
I am using facric.js and JsBarcode. Thanks for the help in advance. If there is any other better way kindly suggest
you can use fabric.Image.fromURL and pass the barcode canvas data as url.
DEMO
$(document).ready(function() {
JsBarcode('#barcode', "1234", {
width: 4,
height: 20 //displayValue: false
});
var canvas = new fabric.Canvas("labelDesigner");
var bc = document.getElementById("barcode");
fabric.Image.fromURL(bc.toDataURL(),function(img){
img.set({
left: 0,
top: 0
})
canvas.add(img);
})
});
canvas[id^=c], div[id=output] {
border: 1px solid red;
}
canvas[id=buffer] {
border: 1px dotted green;
}
#output {
padding: 15px;
}
#output img {
border: 1px dotted blue;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsbarcode/3.9.0/JsBarcode.all.min.js"></script>
<canvas id="barcode"></canvas>
<canvas id="labelDesigner" width="410" height="200" style="border:1px solid #000000;margin-left:200px"></canvas>
The drawImage-function can take another canvas as an argument, so you do not have to convert to an image inbetween. However, it always requires the x and y coordinates as the second and third parameters.
You had also swapped your c and canvas variables:
$(document).ready(function() {
JsBarcode('#barcode', "1234", {
width: 4,
height: 20//displayValue: false
});
var bc = document.getElementById("barcode");
var c = document.getElementById("labelDesigner");
var context = c.getContext("2d");
context.drawImage(bc, 0, 0);
});

HTML attributes (width, height) of canvas change independently

I have some script on JS, it change width and height of canvas element:
function RefreshSizes (canvas) {
var temp_width = 320;
var temp_height = 240;
document.getElementById(canvas).setAttribute('width', temp_width);
document.getElementById(canvas).setAttribute('height', temp_height);
}
this functions calling right after canvas initializing.
It works fine on Chrome.
But in FireFox 49 I see that:
What could it be?
UPD#1 Tested code of BukkitmanPlays MCPE
UPD#2
Full CSS for canvas:
element {
width: 320px;
height: 240px;
}
.canvas {
border: 3px solid #E0E0E0;
z-index: 0;
position: relative;
}
html {
font: 10px/10px arial;
}
some code on one browser isn't the same on another browser, so in this case, what I would do:
function RefreshSizes (canv) {
var temp_width = 320;
var temp_height = 240;
var canvas = canv;
canvas.width = temp_width;
canvas.height = temp_height;
}
I'm sure this will work

Merging javascript code with canvas animation

I’m trying to ‘merge’ some javascript code that changes pictures with a canvas that has also animation to it, without them cancelling eachother out. In this canvas there are some clouds that move and I want to throw kittens into it.
I understand this this might be a basic HTML5 canvas question but I’m terrible using canvas though. This is the first time I’m really working with it. Whenever I try to implement the code that applies to the kittens, the canvas screen just goes white and nothing shows.
I want to stick with most of the code that I have. I really want to keep the canvas the way it is and change nothing there but add those kittens in there too. Can someone puzzle out for me how to appropiately do this?
I'm guessing I would need to tweak the animation function somehow?
function animate(){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
background.render();
foreground.render();
ctx.restore();
}
var animateInterval = setInterval(animate, 30);
}
The code is all in a FIDDLE, because it’s too much to show it on here.
EDIT: To be clear, I'm asking for the pictures of the kittens to be laid over the canvas, but I want the clouds in the canvas to overlay the kittens.
What you need to know is that the canvas element is transparent by default. So notice the minor change I made here :
body{
background:#667;
}
canvas {
width:50vw;
height:50vh;
margin-left: 20%;
}
#image {
border:#000 1px solid;
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
width: 33%;
height: 100%;
position: relative;
top:140px;
z-index: -1;
}
#my_canvas{
border:#000 1px solid;
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
width: 33%;
height: 100%;
}
<link href="css.css" rel="stylesheet" type="text/css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img src="" id="image"/>
<script>
var bg = new Image();
bg.src = "http://proverum.ru/templates/oblaka/img/cloud.png";
var fg = new Image();
fg.src = "http://www.tourisme-fumel.com/img_interf/meteo/cloudy.png";
function initCanvas(){
var lastTick = 0;
var position = { x:0, y:0 };
var rain = document.getElementById('rain');
var ctx = document.getElementById('my_canvas').getContext('2d');
var canvas_container = document.getElementById('my_canvas2');
var cW = ctx.canvas.width, cH = ctx.canvas.height;
function Background(){
this.x = 0, this.y = 0, this.w = bg.width, this.h = bg.height;
this.render = function(){
ctx.drawImage(bg, this.x--, 0);
if(this.x <= -250){
this.x = 0;
}
}
}
var background = new Background();
var image1 = "http://www.catsofaustralia.com/images/three_kittens.jpg";
var image2 = "http://thecatpalace.com.au/wp-content/uploads/2015/05/kitten1.jpg";
var image3 = "http://www.keepingkittens.com/images/cute-little-kitten-minka-rose-the-real-cinderella-story-21652512.jpg";
$(function() {
$("#image").prop("src", image1);
setInterval(function() {
$("#image").prop("src", image2);
setTimeout(function() {
$("#image").prop("src", image2);
setTimeout(function() {
$("#image").prop("src", image3);
}, 50);
setTimeout(function() {
$("#image").prop("src", image1);
}, 500);
}, 10);
}, 5000);
});
function Foreground(){
this.x = 0, this.y = 0, this.w = fg.width, this.h = fg.height;
this.render = function(){
ctx.drawImage(fg, this.x--, 0);
if(this.x <= -499){
this.x = 0;
}
}
}
var foreground = new Foreground();
function animate(){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
background.render();
foreground.render();
ctx.restore();
}
var animateInterval = setInterval(animate, 30);
}
window.addEventListener('load', function(event) {
initCanvas();
});
</script>
</head>
<body>
<canvas id="my_canvas" width="611" height="864"></canvas>
<h1 id="status"></h1>
</body>
</html>
I just removed the
background:#FFF;
from the canvas css and the canvas became transparent.
How you will line up the canvas over the kittens is up to you, I just quickly used position:relative and z-index to prove my point.

Categories