In my project, a canvas element shows a joystick. Through mouse/touch events, the canvas is updated to look like the user is moving the joystick. This works fine. The coordinates are held in an object like so:
scope.point = {
x: 0,
y: 0,
};
And I've added this HTML to show it to the user:
<span>X:{{point.x.toFixed(2)}} Y:{{point.y.toFixed(2)}}</span>
The problem is, when the values of scope.point.x and scope.point.y are changed (in the mouse/touch event handlers), they don't get updated in the HTML. The only solution seems to be to add:
scope.$apply()
//or
scope.$digest()
to the render loop. This does work, but seems inelegant (imho) and makes performance dip noticeably, as expected.
Is there any other solution?
Thanks in advance.
PS: Though I don't think it's relevant, for reference this is the event handler code, and the render loop:
//handles mouse or touch movement on joystick
scope.mouseMove = function(evt) {
if (leftClick == 1) { //check if left mouse button down or touch
// get cursor or touch coordinates, saved in point object.
if (evt.type == 'touchstart' || evt.type == 'touchmove') {
scope.point.x = evt.targetTouches[0].pageX - joystick.offsetLeft;
scope.point.y = evt.targetTouches[0].pageY - joystick.offsetTop;
} else {
scope.point.x = evt.pageX - joystick.offsetLeft - 3;
scope.point.y = evt.pageY - joystick.offsetTop - 3;
};
//make coordinates relative to canvas center
scope.point = GeometrySrv.centerCoord(scope.point, joystick);
//if Directional Lock is ON, enforce
if (scope.lockMode != "fullAnalog") {
scope.point = GeometrySrv.forceDirectionLock(scope.point.x, scope.point.y, scope.lockMode);
};
// force coordinates into maxRadius
if (!GeometrySrv.isInsideCircle(scope.point.x, scope.point.y, maxRadius)) {
scope.point = GeometrySrv.forceIntoCircle(scope.point.x, scope.point.y, maxRadius);
};
//send coordinates back to server (websocket)
updateJoystick(scope.point, scope.lockMode);
};
};
function renderLoop() {
//erases previous joystick position
resetJoystick();
// erases previous vector
resetVector();
//change coordinates to canvas reference
scope.point = GeometrySrv.canvasCoord(scope.point, joystick);
DrawSrv.drawLineFromCenter(joystickctx, scope.point.x, scope.point.y);
if (scope.showVector) {
DrawSrv.drawLineFromCenter(vectorctx, scope.point.x * vector.width / joystick.width, scope.point.y * vector.width / joystick.width);
};
//redraw joystick position
DrawSrv.drawCircle(joystickctx, scope.point.x, scope.point.y, radius, maxRadiusBGColor);
//change back to relative coordinates
scope.point = GeometrySrv.centerCoord(scope.point, joystick);
//scope.$digest();
//call renderLoop every 15ms (60fps)
renderReq = requestAnimationFrame(renderLoop);
};
I would just make a filter, that way you still bind to the object, but your filter displays the object in a particular manner.
[your angular module].filter('toFixed', [function () {
return function (input) {
if (typeof input.toFixed == 'function')
return input.toFixed(2);
return input;
};
}]);
And then bind it in HTML:
<span>X:{{point.x | toFixed}} Y:{{point.y | toFixed}}</span>
$apply
calls $rootScope.$digest
internally, so use $diggest
on local scope for better performance. For best performance abandon data binding and manipulate DOM directly. You can do this from own angular directive .
Read about $applyAsync() - it allows you to queue $digest() cycles and throttle them about every 10ms.
It is also is smart enough to know if Angular is already in a $digest() loop avoiding that annoying error that you'll get calling $apply() twice before $digest() has completed.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.