HTML5 canvas drawing in WebView
Hi, I have pulled together a simple html5 canvas drawing implementation, and am trying to get it to work with a webview. (It works fine in Safari on the desktop, for example). However, there seems to be a conflict between the 'mousemove' canvas event and the scrolling events on the Ti webview.
The code is below for the Ti js file and the html file. Any ideas on this? The docs indicate:
>"Since a webview internally wants to handle its own events, scrolling and other related touch events against it's own view surface, you cannot have both Titanium style events against the webview instance and internal Javascript events in the DOM. You must choose between one or the other."
How do I choose between one or the other? If there is a way to choose, I choose the DOM events :) How can I enforce this in Titanium? I've tried preventDefaults() on a touchmove (for which I couldn't find any docs, btw) but that basically shut everything down.
Thanks!
I've searched through the forum, and noted the following posts:
scrolling a webview,
simple drawing tool
foo.js
var win = Ti.UI.currentWindow;
var webView = Ti.UI.createWebView
({
url:'foo.html',
backgroundColor:'transparent',
width:300,
height:300
});
win.add( webView );
and foo.html
<html>
<body onload="init()">
<canvas id="canvas" class="canvasStyle" width="300" height="300" style="border:1px solid blue"></canvas>
<script type="text/javascript">
canvas = document.getElementById( 'canvas' );
var context = canvas.getContext( '2d' );
context.fillStyle = '#00f';
context.fillRect( 50, 50, 200, 200 ); // be sure something is drawing into canvas
function onMouseMoveOnCanvas( event )
{
if ( canvas.drawing )
{
// var mouseX=event.clientX;
// var mouseY=event.clientY;
var mouseX=event.layerX
var mouseY=event.layerY
if ( canvas.pathBegun == false )
{
context.beginPath();
canvas.pathBegun = true;
}
else
{
context.lineTo( mouseX, mouseY );
context.stroke();
}
}
}
function onMouseClickOnCanvas( event )
{
canvas.drawing = !canvas.drawing;
if ( canvas.drawing ) // reset the path when starting over
{
canvas.pathBegun = false;
}
}
function init()
{
context.strokeStyle = '#f00';
context.lineWidth = 8;
canvas.addEventListener( 'mousemove', onMouseMoveOnCanvas, false );
canvas.addEventListener( 'mousedown', onMouseClickOnCanvas, false );
}
</script>
</body>
</html>
7 Answers
-
Accepted Answer
hi, I tried some things which seem to work (in the simulator at least). Firstly add the following meta tag in the head part of the html, to instruct the webview to scale the html accordingly and to prevent user-scaling with pinching.
<head> <meta name="viewport" content = "width = device-width, initial-scale = 1, minimum-scale = 1, maximum-scale = 1, user-scalable = no" /> </head>
Secondly I browsed the MooTools touch git library repository to see how this library handles the touchevents and the gathering of the mouse(touch) coordinates.
A few things become clear from that sourcecode:
- the use of the 'touchstart', 'touchmove' and 'touchend' events
- the 'getPage' function which calculates the x and y coordinates based on the touch event which retrieves them from the event.targetTouches[0] object
These things can be translated to your sourcecode in this way:
First add the getPage function to your source:
function getPage(event){ //when on mobile safari, the coordinates information is inside the targetTouches object if (event.targetTouches) event = event.targetTouches[0]; if (event.pageX != null && event.pageY != null) return {pageX: event.pageX, pageY: event.pageY}; var element = (!document.compatMode || document.compatMode == 'CSS1Compat') ? document.documentElement : document.body; return {pageX: event.clientX + element.scrollLeft, pageY: event.clientY + element.scrollTop}; }
Put these lines in your init function:
canvas.addEventListener( 'touchmove', onMouseMoveOnCanvas); canvas.addEventListener( 'touchstart', onMouseClickOnCanvas); canvas.addEventListener( 'touchend', onMouseClickOnCanvas, false );
And then change your onMouseMoveOnCanvas function:
function onMouseMoveOnCanvas( event ) { if ( canvas.drawing ) { var page = getPage(event); var startX = page.pageX; var startY = page.pageY; if ( canvas.pathBegun == false ) { context.beginPath(); canvas.pathBegun = true; } else { context.lineTo( mouseX, mouseY ); context.stroke(); } } event.preventDefault(); event.stopPropagation(); }
You would probably want to refactor this code, but it is a start for drawing in a webview using the canvas element.
Good luck, gr. Vic
-
Okay, got it! and a big old "D'oh!". UIWebView needs touch events, not mouse events!!
<html> <body onload="init()"> <canvas id="canvas" class="canvasStyle" width="300" height="300" style="border:1px solid blue"></canvas> <script type="text/javascript"> canvas = document.getElementById( 'canvas' ); var context = canvas.getContext( '2d' ); context.fillStyle = '#00f'; context.fillRect( 0, 0, 50, 50 ); // be sure something is drawing into canvas function onMouseMoveOnCanvas( event ) { var touch = window.event.targetTouches[ 0 ] event.preventDefault(); event.stopPropagation(); if ( canvas.drawing ) { // var mouseX=event.clientX; // var mouseY=event.clientY; // var mouseX=event.layerX // var mouseY=event.layerY var mouseX = touch.pageX, var mouseY = touch.pageY if ( canvas.pathBegun == false ) { context.beginPath(); canvas.pathBegun = true; } else { context.lineTo( mouseX, mouseY ); context.stroke(); } } } function onMouseClickOnCanvas( event ) { canvas.drawing = !canvas.drawing; if ( canvas.drawing ) // reset the path when starting over { canvas.pathBegun = false; } } function init() { context.strokeStyle = '#f00'; context.lineWidth = 8; // canvas.addEventListener( 'mousemove', onMouseMoveOnCanvas, false ); // canvas.addEventListener( 'mousedown', onMouseClickOnCanvas, false ); canvas.addEventListener( 'touchmove', onMouseMoveOnCanvas, false ); canvas.addEventListener( 'touchstart', onMouseClickOnCanvas, false ); } </script> </body> </html>
-
hi, I tried some things which seem to work (in the simulator at least). Firstly add the following meta tag in the head part of the html, to instruct the webview to scale the html accordingly and to prevent user-scaling with pinching.
<head> <meta name="viewport" content = "width = device-width, initial-scale = 1, minimum-scale = 1, maximum-scale = 1, user-scalable = no" /> </head>
Secondly I browsed the MooTools touch git library repository to see how this library handles the touchevents and the gathering of the mouse(touch) coordinates.
A few things become clear from that sourcecode:
- the use of the 'touchstart', 'touchmove' and 'touchend' events
- the 'getPage' function which calculates the x and y coordinates based on the touch event which retrieves them from the event.targetTouches[0] object
These things can be translated to your sourcecode in this way:
First add the getPage function to your source:
function getPage(event){ //when on mobile safari, the coordinates information is inside the targetTouches object if (event.targetTouches) event = event.targetTouches[0]; if (event.pageX != null && event.pageY != null) return {pageX: event.pageX, pageY: event.pageY}; var element = (!document.compatMode || document.compatMode == 'CSS1Compat') ? document.documentElement : document.body; return {pageX: event.clientX + element.scrollLeft, pageY: event.clientY + element.scrollTop}; }
Put these lines in your init function:
canvas.addEventListener( 'touchmove', onMouseMoveOnCanvas); canvas.addEventListener( 'touchstart', onMouseClickOnCanvas); canvas.addEventListener( 'touchend', onMouseClickOnCanvas, false );
And then change your onMouseMoveOnCanvas function:
function onMouseMoveOnCanvas( event ) { if ( canvas.drawing ) { var page = getPage(event); var startX = page.pageX; var startY = page.pageY; if ( canvas.pathBegun == false ) { context.beginPath(); canvas.pathBegun = true; } else { context.lineTo( mouseX, mouseY ); context.stroke(); } } event.preventDefault(); event.stopPropagation(); }
You would probably want to refactor this code, but it is a start for drawing in a webview using the canvas element.
Good luck, gr. Vic
-
What is your issue?
-
Ah, sorry if it wasn't clear from my post: Drag/touchmove/mousemove events conflict between the HTML/canvas and the Ti/WebView - when you drag on the WebView it scrolls the webview and (it seems) the events don't make it to the HTML.
My impression from the documentation (quoted above) is that you can have one or the other, not both. This is great! But… how do I have the touch events propagate to the HTML and NOT to the Ti WebView?
Alternatively, how can I disable scrolling on the WebView without disabling it for the HTML?
Thanks!
-
try this:
function onMouseMoveOnCanvas( event ) { if ( canvas.drawing ) { // var mouseX=event.clientX; // var mouseY=event.clientY; var mouseX=event.layerX var mouseY=event.layerY if ( canvas.pathBegun == false ) { context.beginPath(); canvas.pathBegun = true; } else { context.lineTo( mouseX, mouseY ); context.stroke(); } } // add this here or at the start of the function event.preventDefault(); event.stopPropagation(); }
-
what ti sdk version do you use?