Use Paper.js to draw on canvas


#1

Hi all, I am trying to integrate the [Paper.js][1] javascript library to my ionic app, to draw vectors on canvas, [like in this example][1].

  • I have included my js library on my index.html : <script src="lib/paper/dist/paper-full.js"></script>
  • I have added the [examples JS function][1] (onMouseDown(event), onMouseUp(event)) on my index.html (for testing)
  • I have added the canvas tag on my html template

:x: Problem : Touch event (ionic on apk) or Mouse event (ionic serve on chrome) are not caught by my canvas…
:question: Question : Is there a way to handle Papers.js (or other JS library) events, instead of handle Ionic classic events (that are relative to app navigation)…?

(if it’s not clear enough, please tell me)
[1]: http://paperjs.org/tutorials/geometry/vector-geometry/#vektor.js


Events on canvas : different behaviour serve / device
#2

Are you using Paper.js tool to handle mouse/touch events? Check out this example


#3

Did you get this to work? I have the problem, that I can’t recognize the touch events in EaselJS.


#4

Hi @ekoz,

Is there any update on this topic please ? How did you integrate it on Ionic and does it work?


#5

Sorry for the delay!! Yes it works now, here is how I did :

  1. Include the paperJS library on the index.html :

    <script src="lib/paper/dist/paper-full.js">

  2. Create an ionic view with a canvas inside :

    <canvas id="canvas" ng-mousedown="downEvent($event)" on-touch="downEvent($event)" ng-mousemove="dragEvent($event)" on-drag="dragEvent($event)" ng-mouseup="upEvent($event)" on-release="upEvent($event)" height="400px" width="400px"> </canvas>

  3. Copy the paperJS “vector” function code inside of your controller :

    [click on the source button, top right canvas corner][1]

  4. Add this code to your controller :

    init(); function init(){ paper.install(window); paper.setup('canvas'); // your canvas html id }

  5. Change the JS code to the angular syntax :

    function onMouseDown(event) :arrow_right: $scope.downEvent = function(event)
    function onMouseDrag(event) :arrow_right: $scope.dragEvent = function(event)
    function onMouseUp(event) :arrow_right: $scope.upEvent = function(event)

  6. The event object structure from paperJS is not the same when runing on Ionic, On each 3 gesture function above I called :

    $scope.downEvent = function(event){ event.point = getPoint(event); ... ... ...

    function getPoint(event) { try { // on android return new Point(event.gesture.center.pageX, event.gesture.center.pageY - 44) // 44 = header bar pixel height } catch (e) { // on ionic serve, brower return new Point(event.x, event.y - 44); } }

  7. paperJS vector operations doesn’t work on my app, I replaced many times operations call:

    event.point - vectorStart :arrow_right: new Point(event.point.x - vectorStart.x, event.point.y - vectorStart.y)

  8. Here is the result, I can provide my code if someone need it

:question: So the only solution I found was to specify the gesture functions to be called in the canvas, if someone knows another solution…
[1]: http://paperjs.org/tutorials/geometry/vector-geometry/#vektor.js


#6

ekoz, I followed your steps to add the code into ionic app, but it is not working on android phone.
Can you please share complete code?


#7

Here is a starter for those interested in implementing a drawing feature with their ionic app.


#8

Hey guys I recently made another html canvas starter this time for ionic 2 apps. Check it out


#9

I’ve been searching for a digital signature plugin for my Ionic application. I think I can use your code. Do you mind sharing the full code with me.


#10

Controller :

angular.module('starter.controllers', [])

.controller('AppCtrl', function($scope, $ionicModal, $timeout, $stateParams, $ionicPlatform) {


    var values = {
        fixLength: false,
        fixAngle: false,
        showCircle: false,
        showAngleLength: true,
        showCoordinates: false
    };

    var vectorStart, vector, vectorPrevious;
    var vectorItem, items, dashedItems, dashItem;

    /*
     * DRAW ANGLE
     */

     function drawAngle(center, vector, label) {
        console.log("drawAngle")
        var radius = 25, threshold = 10;
        //if (vector.length < radius + threshold || Math.abs(vector.angle) < 15)
        //    return;
        var from = new Point(radius, 0);
        var through = from.rotate(vector.angle / 2);
        var to = from.rotate(vector.angle);
        var end = new Point(center.x + to.x,center.y + to.y);

        var v1 = new Point(radius + threshold, 0);

        dashedItems.push(new Path.Line(center,
            new Point(center.x + v1.x, center.y + v1.y)));
        dashedItems.push(new Path.Arc(new Point(center.x + from.x, center.y + from.y), new Point(center.x + through.x, center.y + through.y), end));
        var arrowVector = to.normalize(7.5).rotate(vector.angle < 0 ? -90 : 90);
        dashedItems.push(new Path([
            new Point(end.x + arrowVector.rotate(135).x, end.y + arrowVector.rotate(135).y),
            end,
            new Point(end.x + arrowVector.rotate(-135).x, end.y + arrowVector.rotate(-135).y)
            ]));

        var p1 = through.normalize(radius + 10);
        var p2 = new Point(0, 3);
        var p3 = new Point(p1.x + p2.x, p1.y + p2.y);

            // Angle Label
            var text = new PointText(new Point(center.x + p3.x, center.y + p3.y));
            text.content = Math.floor(vector.angle * 100) / 100 + '°';
            text.fillColor = 'orange';
            items.push(text);
            console.log(text)
        }

    /*
     * DRAW LENGTH
     */

     function drawLength(from, to, sign, label, value, prefix) {
         var lengthSize = 5;
         if ((to - from).length < lengthSize * 4)
          return;
      var vector = to - from;
      vector = new Point(to.x - from.x, to.y - from.y)
      var awayVector = vector.normalize(lengthSize).rotate(90 * sign);
      var upVector = vector.normalize(lengthSize).rotate(45 * sign);
      var downVector = upVector.rotate(-90 * sign);
      var lengthVector = vector.normalize(
       vector.length / 2 - lengthSize * Math.sqrt(2));
      var line = new Path();
      line.add(new Point(from.x + awayVector.x, from.y + awayVector.y));
      line.lineBy(upVector);
      line.lineBy(lengthVector);
      line.lineBy(upVector);
      var middle = line.lastSegment.point;
      line.lineBy(downVector);
      line.lineBy(lengthVector);
      line.lineBy(downVector);
      dashedItems.push(line);
      if (label) {
    		// Length Label
    		var textAngle = Math.abs(vector.angle) > 90
            ? textAngle = 180 + vector.angle : vector.angle;
    		// Label needs to move away by different amounts based on the
    		// vector's quadrant:
    		var away = (sign >= 0 ? [1, 4] : [2, 3]).indexOf(vector.quadrant) != -1
            ? 8 : 0;
            value = value || vector.length;
            var v1 = awayVector.normalize(away + lengthSize);
            var v2 = middle;
            var text = new PointText({
               point: new Point(v1.x + v2.x, v1.y + v2.y),
               content: (prefix || '') + Math.floor(value * 1000) / 1000,
               fillColor: 'orange',
               justification: 'center'
           });
            text.rotate(textAngle);
            items.push(text);
        }
    }

    /*
     * EVENTS
     */

     $scope.downEvent = function(event){
         event.point = getPoint(event);
         console.log("downEvent " + event.point)
         vectorStart = event.point;
     }

     function processVector(event, drag) {
        vector = event.point - vectorStart;
        vector = new Point(event.point.x - vectorStart.x, event.point.y - vectorStart.y)
        if (vectorPrevious) {
            if (values.fixLength && values.fixAngle) {
                vector = vectorPrevious;
            } else if (values.fixLength) {
                vector.length = vectorPrevious.length;
            } else if (values.fixAngle) {
                vector = vector.project(vectorPrevious);
            }
        }
        drawVector(drag);

    }

    function drawVector(drag) {
        if (items) {
            for (var i = 0, l = items.length; i < l; i++) {
                items[i].remove();
            }
        }
        if (vectorItem)
            vectorItem.remove();
        items = [];
        var arrowVector = vector.normalize(10);
        var end = vectorStart + vector;
        var end = new Point(vectorStart.x + vector.x, vectorStart.y + vector.y);
        vectorItem = new Group([
            new Path([vectorStart, end]),
            new Path([
                end + arrowVector.rotate(135),
                end,
                end + arrowVector.rotate(-135)
                ])
            ]);
        vectorItem.strokeWidth = 0.75;
        vectorItem.strokeColor = '#e4141b';
        // Display:
        dashedItems = [];
        // Draw Circle
        if (values.showCircle) {
            console.log("draw circle")
            dashedItems.push(new Path.Circle({
                center: vectorStart,
                radius: vector.length
            }));
        }
        // Draw Labels
        if (values.showAngleLength) {
            drawAngle(vectorStart, vector, !drag);
            //if (!drag) {
                drawLength(vectorStart, end, vector.angle < 0 ? -1 : 1, true);
            //}
        }
        var quadrant = vector.quadrant;
        if (values.showCoordinates && !drag) {
            drawLength(vectorStart, vectorStart + [vector.x, 0],
                [1, 3].indexOf(quadrant) != -1 ? -1 : 1, true, vector.x, 'x: ');
            drawLength(vectorStart, vectorStart + [0, vector.y],
                [1, 3].indexOf(quadrant) != -1 ? 1 : -1, true, vector.y, 'y: ');
        }
        for (var i = 0, l = dashedItems.length; i < l; i++) {
            var item = dashedItems[i];
            item.strokeColor = 'black';
            item.dashArray = [1, 2];
            items.push(item);
        }
        // Update palette
        values.x = vector.x;
        values.y = vector.y;
        values.length = vector.length;
        values.angle = vector.angle;
    }

    var first = true;

    function getPoint(event) {
        try {
            return new Point(event.gesture.center.pageX, event.gesture.center.pageY)
        }
        catch (e) {
            return new Point(event.x, event.y);
        }
    }

    $scope.dragEvent = function(event) {
     event.point = getPoint(event);
     console.log("dragEvent " + event.point);
     if (vectorStart) {
         processVector(event, true);
     }
 }

 $scope.upEvent = function(event) {
     event.point = getPoint(event);
     console.log("upEvent " + event.point)
     processVector(event, false);
 }

 init();
 function init(){
    paper.install(window);
    paper.setup('canvas');
}

})

Template :

<ion-view view-title="Tool">

  <ion-content>

    <canvas id="canvas"
    ng-mousedown="downEvent($event)"
    on-touch="downEvent($event)"
    ng-mousemove="dragEvent($event)"
    on-drag="dragEvent($event)"
    ng-mouseup="upEvent($event)"
    on-release="upEvent($event)"
    height="374px"
    width="375px"

    style="background-image: url('templates/img.png');">
</canvas>

</ion-content>

</ion-view>

375*374 Image :


#11

I am actually very interested by your code if you can share it !
Can you ?