0

I started creating a little remote control App using Angular. I created a joystick directive which is using the canvas.

I wrapped it in an angular directive and it works quite nice. You can see an example here (NOTE: Only touch, no mouse events yet):

http://jsfiddle.net/wjE2B/3/

What i want to do is control a car which basicall means i will keep the joystick dragged all the time and additionally i want to control another button e.g. for the horn. I would prefer using a simple button here.

But all the handlers like ng-click stop working if i drag the joystick around. I can listen to other touch events (e.g. simply add another joystick, works independently) on the canvas and of course other canvas, but can i simply listen to other events like touch somehow? How do i achieve this the most angular / simplest way?

Here the code from the fiddle:

angular.module('myApp').directive('joystick', function() {

    function joystickController ($scope) {

    }

    return {
        restrict : 'E',
        controller : ['$scope', function ($scope) {
            return joystickController($scope);
        }],
        scope : {
            // Using primitives here did not work, so we use an Object, see: http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
            position : '='
        },
        template : '<canvas class="joystickCanvas"></canvas>',
        link : function(scope, element) {

            var joystickHeight = 200;
            var joystickWidth  = 200;

            var center = {
                x : joystickHeight / 2,
                y : joystickWidth / 2
            };

            var radiusCircle = 35;
            var radiusBound = 50;

            // Canvas and context element
            var container = element[0];
            var canvas = container.children[0];
            var ctx = canvas.getContext('2d');

            // Id of the touch on the cursor
            var cursorTouchId = -1;
            var cursorTouch = {
                x : center.x,
                y : center.y
            };

            function resetCanvas() {
                canvas.height = joystickHeight;
                canvas.width = joystickWidth;
            }

            function onTouchStart(event) {
                var touch = event.targetTouches[0];
                cursorTouchId = touch.identifier;
                cursorTouch = {
                    x : touch.pageX - touch.target.offsetLeft,
                    y : touch.pageY - touch.target.offsetTop
                };
            }

            function onTouchMove(event) {
                // Prevent the browser from doing its default thing (scroll, zoom)
                event.preventDefault();
                for(var i = 0; i < event.changedTouches.length; i++){
                    var touch = event.changedTouches[i];

                    if(cursorTouchId === touch.identifier)
                    {
                        cursorTouch = {
                            x : touch.pageX - touch.target.offsetLeft,
                            y : touch.pageY - touch.target.offsetTop
                        };

                        var scaleX = radiusBound / (cursorTouch.x - center.x);
                        var scaleY = radiusBound / (cursorTouch.y - center.y);

                        if(Math.abs(scaleX) < 1) {
                            cursorTouch.x = Math.abs(cursorTouch.x - center.x) * scaleX + center.x;
                        }

                        if (Math.abs(scaleY) < 1) {
                            cursorTouch.y = Math.abs(cursorTouch.y - center.y) * scaleY + center.y;
                        }

                        scope.$apply(
                            scope.position = {
                                x : Math.round(((cursorTouch.x - center.x)/radiusBound) * 100),
                                y : Math.round(((cursorTouch.y - center.y)/radiusBound) * -100)
                            }
                        );

                        break;
                    }
                }

            }

            function onTouchEnd() {

                cursorTouchId = -1;

                scope.$apply(
                    scope.position = {
                        x : 0,
                        y : 0
                    }
                );

                cursorTouch.x = center.x;
                cursorTouch.y = center.y;
            }

            function draw() {
                // Clear the canvas
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                ctx.beginPath();
                ctx.strokeStyle = 'cyan';
                ctx.lineWidth = 5;
                ctx.arc(center.x, center.y, radiusCircle, 0, Math.PI*2, true);
                ctx.stroke();

                ctx.beginPath();
                ctx.strokeStyle = 'cyan';
                ctx.lineWidth = 2;
                ctx.arc(center.x, center.y, radiusBound, 0, Math.PI*2, true);
                ctx.stroke();

                ctx.beginPath();
                ctx.strokeStyle = 'cyan';
                ctx.lineWidth = 2;
                ctx.arc(cursorTouch.x, cursorTouch.y, radiusCircle, 0, Math.PI*2, true);
                ctx.stroke();

                requestAnimFrame(draw);
            }

            // Check if touch is enabled
            var touchable = true;

            if(touchable) {
                canvas.addEventListener( 'touchstart', onTouchStart, false );
                canvas.addEventListener( 'touchmove', onTouchMove, false );
                canvas.addEventListener( 'touchend', onTouchEnd, false );

                window.onorientationchange = resetCanvas;
                window.onresize = resetCanvas;
            }

            // Bind to the values from outside as well
            scope.$watch('position', function(newval) {
                cursorTouch = {
                    x : ((newval.x * radiusBound) / 100) + center.x,
                    y : ((newval.y * radiusBound) / -100) + center.y
                };
            });

            resetCanvas();
            draw();

        }

    };

});
pfried
  • 5,000
  • 2
  • 38
  • 71
  • Have you tried using **[`ngTouch`](https://docs.angularjs.org/api/ngTouch/)** ? – gkalpak Jun 22 '14 at 10:01
  • From what i can see ngTouch only provides the swipe directive which is useless in this case – pfried Jun 22 '14 at 10:04
  • Take a closer look :) I don't know if it will work in your case, but it has more to offer the the swipe... – gkalpak Jun 22 '14 at 10:14
  • I took a look at it and recognized it changes the `ngClick` behaviour, but even if including the `ngTouch` module, the behaviour stays the same – pfried Jun 22 '14 at 10:24
  • I added a `console.log` inside the `ngTouch`'s `ngClick`directive and the `touchstart` gets triggered, but the click doesnt happen for some reason.`touchend` gets triggered too. – pfried Jun 22 '14 at 10:29
  • Could it be that it is platform-specific ? Are you sure that (outside of Angular) you can have dragging and clicking at the same time ? Besides, even the `touchend` event should be enough, right ? – gkalpak Jun 22 '14 at 11:01
  • That is not necessary angular specific but the `ngTouch` adds some logic between the `touchstart` and `touchend` event, i have to narrow it down a bit further to tell what keeps the `click` event from beeing triggered in this case – pfried Jun 22 '14 at 11:08
  • I mean, if `touchend` is properly dispatched on the button, then you can bind to that (instead of 'click'). – gkalpak Jun 22 '14 at 11:12
  • yes i can do that, but i thought maybe i am missing something. There should be a native angular way to do it. I think i will create a directive like `ng-click` – pfried Jun 22 '14 at 12:40
  • Again, I am not sure if this is an Angular limitation or a limitation of the device/application (so Angular couldn't do anything "natively"). In any case, I don't think Angular's touch support is its strong-point (as of now at least). An `ngClick`-like directive sounds like a good choice :) – gkalpak Jun 22 '14 at 14:34
  • I created an issue on the angular repo: https://github.com/angular/angular.js/issues/7935 , i explain there why the ngTouch doesnt work the way i want it to. But i dont know if this is by design, so i have to wait for a reply – pfried Jun 23 '14 at 05:45

0 Answers0