paper.js how to set up multiple canvases using only javascript
Asked Answered
I

7

14

I'm trying to use paper.js in a webapp, but I've been unable to get it to work with multiple canvases. It's like the scopes are getting mixed up between the canvases, so when I intend to draw on canvas 1, it appears on canvas 2.

In each view, I'm initialize the paper like this:

this.mypaper = new paper.PaperScope();
this.mypaper.setup($("myCanvasId")[0]);

When I create new paper objects, I use what should be the local scope:

var circle = new this.mypaper.Path.Circle(10, 10, 5);

However, when I create a circle in view1, it draws it in view2 instead.

I've done a lot of reading, but I still haven't found a clear explanation of how to setup multiple paperscopes or how to isolate views from each other.

Does anyone know how to use multiple canvases with paper.js correctly?


EDIT: I've created a jsFiddle to illustrate the problem: http://jsfiddle.net/94RTX/1/

Iced answered 31/5, 2013 at 20:57 Comment(0)
B
15

I haven't worked with Paper.js extensively, but it seems that each call to Path isn't using the PaperScope from which it's being accessed, but the global paper object. So if you overwrite paper to your desired PaperScope before each instantiation it should work.

See my updated fiddle.

Bereft answered 3/6, 2013 at 16:21 Comment(3)
This does not work if you call a resize event later... Will try to create a fiddleFarhi
I feel it's worth mentioning the pattern I use. It is to have a paper accessor function, like function getPaper() { return window.paper = mypaper; } That way you can be sure whenever you use paper the global variable will be set appropriately.Runabout
Fiddle was not working due to invalid paper.js file URL. Fixed here: jsfiddle.net/sy3L9nh2Latticed
F
10

I actually solve this a bit differently:

var scope1 = new paper.PaperScope();
var scope2 = new paper.PaperScope();

When I want to draw in scope1:

scope1.activate();
// now I draw

Similarly when I want to draw in scope 2

scope2.activate();
// now I draw
Foretoken answered 19/11, 2016 at 7:8 Comment(0)
M
5

In order to more explicitly manage which paperscope you are adding items to, you might consider setting the option insertItems to false.

  var paper1 = new paper.PaperScope();
  paper1.setup(canvasElement);
  paper1.settings.insertItems = false; 

That way, when you create new paper items, they are not automatically added to the paper. So, no matter which scope your paper item was created in, you still decide to add it to one paper or another. For example, you can theoretically do:

  // create a second scope 
  var paper2 = new paper.PaperScope();
  // create a circle in the first scope
  var myCircle = new paper1.Path.Circle(new paper1.Point(100, 70), 50);
  myCircle.fillColor = 'black';
  // add that circle to the second scope's paper 
  paper2.project.activeLayer.addChild(myCircle);
Motel answered 28/1, 2017 at 0:48 Comment(0)
T
4

Use arrays to separated your papers.

this.mypapers = []

var mypaper = new paper.PaperScope();
mypaper.setup($("myCanvasId")[0]);

mypapers.push(mypaper);

mypaper = new paper.PaperScope();
mypaper.setup($("myCanvasId")[1]);

mypapers.push(mypaper);

var circle = new this.mypapers[0].Path.Circle(10,10,5);
var circle2 = new this.mypapers[1].Path.Circle(10,10,10);

EDIT: I've update your js fiddle: http://jsfiddle.net/94RTX/3/

Apparently each setup erase the previous one, so the solution is to do it in this order:

setup canvas 1-> draw canvas 1 -> setup canvas 2 -> draw canvas 2
Teredo answered 31/5, 2013 at 22:18 Comment(2)
Hmmm, still doesn't seem to work. I've updated my original question with a jsfiddle so you can see what I'm talking about.Iced
Unfortunately, this way only works if you do all your drawing code in one step. Since I also have asynchronous callbacks that need to draw things, doing it this way isn't an option.Iced
F
1

I think I found the solution: I expanded the fiddle from @freejosh to also work with callbacks (e.g. resize): The trick is to re-fetch the correct scope inside the callback function:

http://jsfiddle.net/rassoh/mx9n3vsf/7/

var mypapers = [];

initPaper(0, $("#canvas1")[0]);
initPaper(1, $("#canvas2")[0]);

function initPaper(id, canvasElement) {
    var mousePosition = new paper.Point(0,0);
    mypapers[id] = new paper.PaperScope();
    paper = mypapers[id];
    paper.setup(canvasElement);
    var myCircle;

    createCircle = function() {
        if( "undefined" !== typeof myCircle) {
            myCircle.remove();
        }
        // this function is called on resize, so we have to re-fetch the scope!
        paper = mypapers[id];
        myCircle = new paper.Path.Circle(30, 30, 20);
        var lightRed = new paper.Color(1, 0.5, 0.5);
        var lightBlue = new paper.Color(0.5, 0.5, 1);
        myCircle.style = {
            fillColor: id === 0 ? lightRed : lightBlue,
            strokeColor: "black"
        };
    }
    createCircle();

    var tool = new paper.Tool();
    tool.onMouseMove = function(event) {
        mousePosition = event.lastPoint;
    };
    paper.view.onFrame = function() {
        if( "undefined" === typeof myCircle) {
            return;
        }
        var dist = mousePosition.subtract( myCircle.position );
        dist = dist.divide( 3 );
        myCircle.position = myCircle.position.add( dist );
    };
    paper.view.onResize = function() {
        createCircle();
    };
}

$(window).resize(function() {
    var width = $(".container").width() / 2;
    var height = $(".container").height();
    // this automatically triggeres paper's onResize event registered above
    mypapers[0].view.viewSize = new paper.Size( width, height );
    mypapers[1].view.viewSize = new paper.Size( width, height );
});

Please note that I also included a simple interaction with the circles, to also test for correct behaviour there.

Farhi answered 1/5, 2015 at 11:32 Comment(0)
A
1

@freejosh's answer works great for 10.2, but the fiddle still re-uses one canvas when upgrading to the latest version (12.2 as of now):

http://jsfiddle.net/ecneto/86qckn2h/1/

It's a simple fix, you can either downgrade, or make use of scope.activate(), which is feels a lot better than overriding paper:

http://jsfiddle.net/ecneto/n91rdp6z/2/

mypapers[0].activate();
var circle1 = new paper.Path.Circle(30, 30, 20);
circle1.style = {
    fillColor: new paper.Color(1, .5, .5),
    strokeColor: "black"
};

mypapers[1].activate();
var circle2 = new paper.Path.Circle(60, 60, 20);
circle2.style = {
    fillColor: new paper.Color(.5, .5, 1),
    strokeColor: "black"
};
Antheridium answered 15/12, 2019 at 17:47 Comment(0)
B
0

Actually, you only need one Paperscope.

This tutorial said:

When working with JavaScript directly, in most cases one scope will be all that is required. Within this scope, one can still work with multiple projects or views by creating them using the new Project() and new View(canvas) constructors.

paper.setup('canvas1')
paper.setup('canvas2')

paper.projects[0].activate()
// canvas1 is activated
// draw something on canvas1

paper.projects[1].activate()
// canvas2 is activated
// draw something on canvas2

Bascomb answered 26/3, 2021 at 9:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.