A module for making views draggable

You must Login before you can answer or comment on any questions.

Here is a module that will make a view draggable (or resizable depending on the views initial layout.)

/*
 * Testing shows that ignoring every nth event will make this actually
 * work. skipMoves = 2 seems to work with HTC Desire. Needs testing
 * with both faster and slower devices - will probably run horribly
 * in stretch mode on a tablet
 */
exports.makeDraggable = function(view) {
 
    view.addEventListener('touchstart', function(e) {
        // Record where the touch started. Attach the recorded
        // startpoint to the view so that it can be read later
        e.source._event_touchStart = {
            x : e.x,
            y : e.y,
            moveCount : 0,
            skipMoves : 1,
        }
    })
 
    view.addEventListener('touchmove', function(e) {
        var touchStart = e.source._event_touchStart
        touchStart.moveCount++
 
        // only move if we've skipped enough times
        if(!(touchStart.moveCount % (touchStart.skipMoves + 1))) {
            touchStart.moveCount = 0
            // it should be optional which properties
            // to manipulate
            e.source.left += e.x - (touchStart.x)
            e.source.top += e.y - (touchStart.y)
        }
    })
 
    view.addEventListener('touchend', function(e) {
        delete e.source._event_touchStart
    })
};
Here's how you use it:
var win = Ti.UI.createWindow({
    backgroundColor : '#004477',
})
 
 
var viewDraggable = Ti.UI.createView({
    backgroundImage : '/news.png',
    top : 50,
    left : 50,
//  if you set the right and bottom props, the view will resize, not move
//  right : 30,
//  bottom : 30,
    borderWidth : 2,
    borderColor : 'black',
 
})
// Use the appropriate modulepath/name for your setup
var Touch = require('/libm/touch/m_touch')
Touch.makeDraggable(viewDraggable)
 
win.add(viewDraggable)
win.open()
I'm a bit concerned that you'd have to use different values for skipMoves for different phones. If anyone tries this out, I'd love to hear which value works best on their phone.

It works pretty good on my Desire with a skipMoves value of 1, which means only every 2nd event actually drags the view.

If anyone has a better implementation of this functionality I'd love to see it. My implementation is a hack to make it work.

— asked 2 years ago by Esben Maaløe
5 Comments
  • Sorry - this is for Android, I haven't had chance to test it on the iPhone. I'm not sure that the skipping part is necessary on iOS

    — commented 2 years ago by Esben Maaløe

  • and besides on iPhone you have a globalPoint. If one could have a globalPoint that was NOT calculated from the views position, but the actual real globalPoint, I don't think a hack would be need

    — commented 2 years ago by Esben Maaløe

  • Hi Esben

    Its cool i will sure try it out and provide you my feedback. Keep up the good work.

    Regards

    Nikunj

    — commented 2 years ago by Nikunj S

  • Show 2 more comments

1 Answer

Here's a newer version - try fiddling with the dampingFactor and/or the skiMoves values

/*
 * Testing shows that ignoring every nth event will make this actually
 * work. skipMoves = 2 seems to work with HTC Desire. Needs testing
 * with both faster and slower devices - will probably run horribly
 * in stretch mode on a tablet
 */
//var skipMoves = 1, dampingFactor = {1,1} or
//var skipMoves = 0, dampingFactor = {0.5, 0.5} seems to do the trick
// on my HTC Desire
exports.makeDraggable = function(view, options) {
    view.drag = _.extend({
        xProperty : 'left', // You can chose which properties to manipulate
        yProperty : 'top',
        filter : [], // array of functions to filter x/y through - not implemented
        skipMoves : 1, // no. of events to skip ()
        dampingFactor : {
            x : 1, // 1 is neutral damping
            y : 1, // 0.5 seems to be the setting for HTC Desire.
        }
    }, options)
 
    /* Touchstart */
    view.addEventListener('touchstart', function(e) {
        // Record where the touch started. Attach the recorded
        // startpoint to the view so that it can be read later
        e.source._event_touchStart = {
            x : e.x,
            y : e.y,
            moveCount : 0,
        }
    })
    /* Touchmove */
    view.addEventListener('touchmove', function(e) {
        var touchStart = e.source._event_touchStart
        touchStart.moveCount++
 
        var move = {
            x : (e.x - (touchStart.x)) * view.drag.dampingFactor.x,
            y : (e.y - (touchStart.y)) * view.drag.dampingFactor.y,
        }
 
        // only move if we've skipped enough times
        if(!(touchStart.moveCount % (view.drag.skipMoves + 1))) {
            touchStart.moveCount = 0
            e.source[view.drag.xProperty] += move.x
            e.source[view.drag.yProperty] += move.y
        }
    })
    /* Touchend */
    view.addEventListener('touchend', function(e) {
        delete e.source._event_touchStart
    })
};

— answered 2 years ago by Esben Maaløe
answer permalink
2 Comments
  • In the comments I write two diff. values for skipMove as the best for HTC Desire - that's a mistake - the best value for HTC Desire is 1 :)

    — commented 2 years ago by Esben Maaløe

  • ooops - I'm using underscore.js, so you'll get an error in line 2

    view.drag = _.extend({ etc ...

    replace that line with:

    view.drag = {(remember to remove the ending parens as well)

    — commented 2 years ago by Esben Maaløe

Your Answer

Think you can help? Login to answer this question!