Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

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>
— asked August 23rd 2010 by karlo kilayko
  • canvas
  • dom
  • events
  • html5
  • scroll
  • webview
0 Comments

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

    — answered August 24th 2010 by Victor van Rijn
    permalink
    0 Comments
  • 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>
    
    — answered August 24th 2010 by karlo kilayko
    permalink
    1 Comment
    • Big thanks Tamas, for your help and patience!

      And if someone wouldn't mind copying/pasting the answer above, I will mark this closed.

      It's working great on the iPhone and emulator now, btw :)

      — commented August 24th 2010 by karlo kilayko
  • 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

    — answered August 24th 2010 by Victor van Rijn
    permalink
    0 Comments
  • What is your issue?

    — answered August 24th 2010 by Dan Tamas
    permalink
    0 Comments
  • 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!

    — answered August 24th 2010 by karlo kilayko
    permalink
    0 Comments
  • 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(); 
    
    
    
            }
    
    — answered August 24th 2010 by Dan Tamas
    permalink
    3 Comments
    • Hi Tamas, that was a great suggestion, unfortunately it's not working. I've tried to debug (with alerts) and the canvas seems to be getting the mousedown events, but not the mousemove events. Actually with the two event calls you suggest at the bottom of the onMouseMoveOnCanvas func, the webview just scrolls around on touches. On the other hand, with the two calls at the top of the func, the alert will pop up, but then subsequent drags move scroll the webview around.

      I should note that i've only tried this on the emulator; I'll give it a shot on the device as well.

      Thanks for your help!

      — commented August 24th 2010 by karlo kilayko
    • Okay, tried on the device with the same results (I had hoped perhaps there was a difference in an actual touchmove on the device vs a hold and drag with the mouse on the emulator, but no).

      — commented August 24th 2010 by karlo kilayko
    • And, interestingly, I sampled a couple HTML5/canvas drawing test apps from Safari on the iPhone, and it appears that (at least the varieties I checked) have the same problem in Safari: dragging scrolls the web page, rather than being interpreted as a mousemove/touchmove event within the HTML5/canvas js

      — commented August 24th 2010 by karlo kilayko
  • what ti sdk version do you use?

    — answered August 24th 2010 by Dan Tamas
    permalink
    1 Comment
    • Ti SDK is version 1.4. I ~may~ have found a solution, btw, but checking it out still:
      http://www.bennadel.com/blog/1867-Drawing-On-The-iPhone-Canvas-With-jQuery-And-ColdFusion.htm

      — commented August 24th 2010 by karlo kilayko
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.