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):
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();
}
};
});