Page 1 of 3 123 LastLast
Results 1 to 10 of 29

Thread: A Class for "hold down" buttons.

  1. #1
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default A Class for "hold down" buttons.

    I have written a new class which wraps any element to make it into the kind of button that you click and hold to perform a continuous operation.

    While the mouse is held "mousedowned" on the element, the class fires a "fire" event which can be used to change a continuous value, move or scroll an element etc.

    Here's the code embedded in a working example (apart from the fact that you'll need to put in your own URLs for utilities.js and logger.js):

    It's an Ext 0.40+ only class BTW!

    Code:
    <html>
    <head>
    <script type="text/javascript" src="js/utilities.js"></script>
    <script type="text/javascript" src="js/logger.js"></script>
    <script type="text/javascript" src="http://www.yui-ext.com/deploy/yui-ext.0.40-alpha/yui-ext-40a1.php"></script>
    <script type="text/javascript">
    /**
       @class Ext.HoldButton
       @extends Ext.util.Observable
    
       A wrapper class which can be applied to any element. Fires a "fire" event while the
       mouse is pressed. The interval between firings may be specified in the config but
       defaults to 10 milliseconds.
       
       Optionally, a CSS class may be applied to the element during the time it is pressed.
       
       @config el The element to act as a button.
       @config interval The interval between firings of the "fire" event. Default 10 ms.
       @config pressClass A CSS class name to be applied to the element while pressed.
    */
    Ext.HoldButton = function(config)
    {
       Ext.util.Config.apply(this, config, {interval: 10});
       this.el = getEl(this.el);
       this.el.unselectable();
        this.events = {
            /**
             * @event fire
             * Fires on a specified interval during the time the element is pressed.
             * @param {YAHOO.ext.TabPanel} this
             */
            'fire' : new YAHOO.util.CustomEvent('fire')
        };
       this.el.on('mousedown', this.handleMouseDown, this, true);
    };
    
    Ext.extend(Ext.HoldButton, Ext.util.Observable, {
    
       docEl : getEl(document),
    
       handleMouseDown : function() {
          if (this.pressClass)
             this.el.addClass(this.pressClass);
          this.timer = setInterval(this.fire.createDelegate(this), this.interval);
          this.docEl.on("mouseup", this.handleMouseUp, this, true);
          this.el.on("mouseout", this.handleMouseOut, this, true);
       },
       
       fire : function() {
          if (!this.paused) {
             this.fireEvent("fire", this);
          }
       },
       
       handleMouseOut : function() {
          this.paused = true;
          if (this.pressClass)
             this.el.removeClass(this.pressClass);
          this.el.on("mouseover", this.handleMouseReturn, this, true);
       },
       
       handleMouseReturn : function() {
          this.el.removeListener("mouseover", this.handleMouseReturn);
          if (this.pressClass)
             this.el.addClass(this.pressClass);
          this.paused = false;
       },
    
       handleMouseUp : function() {
          clearInterval(this.timer);
          if (this.paused)
             this.el.removeListener("mouseover", this.handleMouseReturn);
          else if (this.pressClass)
             this.el.removeClass(this.pressClass);
          this.el.removeListener("mouseout", this.handleMouseOut);
          this.docEl.removeListener("mouseup", this.handleMouseUp);
          delete this.paused;
       }   
    });
    </script>
    <title>Test HoldButton</title>
    <style type="text/css">
    .red {
        background-color:red;
    }
    </style>
    <script type="text/javascript">
    YAHOO.util.Event.on(window, "load", function() {
    	YAHOO.widget.Logger.enableBrowserConsole();
    	var h = new Ext.HoldButton( {el:"element-id", pressClass:"red"});
    	h.on("fire", function(){YAHOO.log("fired");});
    });
    </script>
    </head>
    <body>
    <span id="element-id">Click and press on me</span>
    </body>
    </html>

  2. #2
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    OK, here's the latest version. It has an optional delay before the continuous event starts firing.

    And, if there is a delay, an optional "immediate" config option which means that one fire of the event occurs oninitial mousedown. So that's perfect for spinners where one click increments by one, and click and hold spins:

    Code:
    <html>
    <head>
    <script type="text/javascript" src="js/utilities.js"></script>
    <script type="text/javascript" src="js/logger.js"></script>
    <script type="text/javascript" src="js/yui-ext.js"></script>
    <script type="text/javascript">
    /**
    	@class Ext.HoldButton
    	@extends Ext.util.Observable
    
    	A wrapper class which can be applied to any element. Fires a "fire" event while the
    	mouse is pressed. The interval between firings may be specified in the config but
    	defaults to 10 milliseconds.
    
    	Optionally, a CSS class may be applied to the element during the time it is pressed.
    
    	@config el The element to act as a button.
    	@config delay The initial delay before the repeating event begins firing.
       				Similar to an autorepeat key delay.
    	@config immediate If an initial delay is requested, force one immediate fire on mousedown
    	@config interval The interval between firings of the "fire" event. Default 10 ms.
    	@config pressClass A CSS class name to be applied to the element while pressed.
    */
    Ext.HoldButton = function(config)
    {
    	Ext.util.Config.apply(this, config, {interval: 10});
    	this.el = getEl(this.el);
    	this.el.unselectable();
        this.events = {
            /**
             * @event fire
             * Fires on a specified interval during the time the element is pressed.
             * @param {Ext.HoldButton} this
             */
            'fire' : new YAHOO.util.CustomEvent('fire')
        };
    	this.fireDelegate = this.fire.createDelegate(this);
    	this.el.on('mousedown', this.handleMouseDown, this, true);
    };
    
    Ext.extend(Ext.HoldButton, Ext.util.Observable, {
    
    /**	@private */
    	docEl : getEl(document),
    
    /**	@private */
    	handleMouseDown : function() {
    		try {
    			this.el.dom.blur();
    		}
    		catch (e) {YAHOO.log(e);}
    		if (this.pressClass)
    			this.el.addClass(this.pressClass);
    		if (this.delay) {
    			if (this.immediate) {
    				this.fireEvent("fire", this);
    			}
    			this.timer = this.fireDelegate.defer(this.delay);
    		}
    		else
    			this.fireDelegate();
    		this.docEl.on("mouseup", this.handleMouseUp, this, true);
    		this.el.on("mouseout", this.handleMouseOut, this, true);
    	},
       
    /**	@private */
    	fire : function() {
    		this.fireEvent("fire", this);
    		this.timer = this.fireDelegate.defer(this.interval);
    	},
       
    /**	@private */
    	handleMouseOut : function() {
    		clearTimeout(this.timer);
    		if (this.pressClass)
    			this.el.removeClass(this.pressClass);
    		this.el.on("mouseover", this.handleMouseReturn, this, true);
    	},
    
    /**	@private */
    	handleMouseReturn : function() {
    		this.el.removeListener("mouseover", this.handleMouseReturn);
    		if (this.pressClass)
    			this.el.addClass(this.pressClass);
    		this.fireDelegate();
    	},
    
    /**	@private */
    	handleMouseUp : function() {
    		clearTimeout(this.timer);
    		this.el.removeListener("mouseover", this.handleMouseReturn);
    		this.el.removeListener("mouseout", this.handleMouseOut);
    		this.docEl.removeListener("mouseup", this.handleMouseUp);
    		this.el.removeClass(this.pressClass);
    	}   
    });
    </script>
    <title>Test HoldButton</title>
    <style type="text/css">
    .red {
        background-color:red;
    }
    .spinner-button {
    	background-color:buttonface;
    	border-width:1px;
    	border-style:solid;
    	border-color:buttonhighlight buttonshadow buttonshadow buttonhighlight;
    	font-size:0.4em;
    	padding:1px 4px 2px 4px;
    	cursor:default;
    }
    .pressed {
    	border-color:buttonshadow buttonhighlight buttonhighlight buttonshadow;
    }
    </style>
    <script type="text/javascript">
    YAHOO.util.Event.on(window, "load", function() {
    	YAHOO.widget.Logger.enableBrowserConsole();
    	var inc = new Ext.HoldButton( {
    		el:"inc",
    		delay:1000,
    		pressClass:"pressed",
    		immediate: true
    	});
    	var dec = new Ext.HoldButton( {
    		el:"dec",
    		delay:1000,
    		pressClass:"pressed",
    		immediate: true
    	});
    	
    	var c = document.getElementById("counter");
    	inc.on("fire", spin.createDelegate(c, [1]));
    	dec.on("fire", spin.createDelegate(c, [-1]));
    });
    function spin(p)
    {
    	var v = parseInt(this.value);
    	if (isNaN(v)) v = 0;
    	this.value = v + p;
    }
    </script>
    </head>
    <body>
    
    
    Click and hold the spinner buttons to autorepeat</p>
    <table cellspacing="0" cellpadding="0">
    	<tr>
    		<td rowspan="2">
    			<input id="counter" value="0">
    		</td>
    		<td>
    			<span class="spinner-button" id="inc">▲</span>
    		<td>
    	</tr>
    	<tr>
    		<td>
    			<span class="spinner-button" id="dec">▼</span>
    		</td>
    	<tr>
    </table>
    </body>
    </html>

  3. #3
    Sencha User
    Join Date
    Apr 2012
    Location
    Austin, Texas
    Posts
    4

    Default

    Hey Animal,

    Pretty cool buttons. Btw, I'm not sure where your little arrows came from, but I can't find them in HTML refs and they don't copy (they paste as ? for me). I replaced them with + and - and set the font to courier for demo purposes, and that works OK. Also, I would remove the dependency on Logger by default, since it's only used in the single catch anyway. One less thing to configure (I just commented it out to get it working -- I don't ever use YUI logger!).

    Thanks!

  4. #4
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    Uparrow is ampersand #9650;

    Downarrow is ampersand #9660;

    The code I pasted contained the correctly formatted HTML entities, but it looks like the PHPBB code tag doesn't make ampersands safe, and the actual glyphs end up in the page. I can't post anything that looks like HTML entities either! But I'm sure you get the idea!

  5. #5
    Sencha User
    Join Date
    Apr 2012
    Location
    Austin, Texas
    Posts
    4

    Default

    Hey, cool -- that's much better! I actually looked briefly at a couple of html refs, but did not see those specific arrow codes, and even in source view they still displayed as arrows. The control works really nicely, thanks again.

  6. #6
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    Bit of help needed here from any mathematics whizzes out there. I'm just a coder - no mathematics!

    I'm trying to get the HoldButton to do easing in. That is, start repeating slowly, and accelerate the rate of firing the longer the button is held.

    So I now have

    Code:
    /**	@private */
    	fire : function() {
    		this.fireEvent("fire", this);
    		this.timer = this.fireDelegate.defer(this.getInterval());
    	},
    	
    	getInterval: function()
    	{
    		if (!this.accelerate)
    			return this.interval;
    		var pressTime = new Date().getTime() - this.mousedownTime;
    		return this.interval; //  How can I calculate gradually decreasing intervals?
    	},
    The interval can be calculated, but I've no idea how to calculate something like this? can anyone help?

  7. #7
    Sencha User
    Join Date
    Apr 2012
    Location
    Austin, Texas
    Posts
    4

    Default

    I'll reply with the obvious... have you tried adapting YUI's anim ocde?
    Code:
        /**
         * Begins slowly and accelerates towards end. (quadratic)
         * @method easeIn
         * @param {Number} t Time value used to compute current value
         * @param {Number} b Starting value
         * @param {Number} c Delta between start and end values
         * @param {Number} d Total length of animation
         * @return {Number} The computed value for the current animation frame
         */
        easeIn: function (t, b, c, d) {
        	return c*(t/=d)*t + b;
        },

  8. #8
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    Yes, I looked at that code.

    It's based upon there being a defined time in which the animation must complete.

    In my code, I'm coming from the other side - the delay depends upon already elapsed time.

    There might be a relationship between these two problems, and the solution might lie in a similar algorithm, but I'm too much of a mathmatical fud to figure it out!

  9. #9
    Sencha User
    Join Date
    Mar 2007
    Location
    Haarlem, Netherlands
    Posts
    1,243

    Default

    Have you tried something like

    Code:
    /**   @private */
       fire : function() {
          this.fireEvent("fire", this);
          this.timer = this.fireDelegate.defer(this.getInterval());
       },
       
       getInterval: function()
       {
          if (!this.accelerate)
             return this.interval;
          var pressTime = new Date().getTime() - this.mousedownTime;
          this.interval *= 1 - (this.mousedownTime/10000);
          return this.interval;
    
          // you should maybe set a stop somewhere cause it will go too fast in the end :)
          // if this calculation doesnt suit maybe try an exponential function? those tend to give nice curves.
       }
    this will give you something like this (with a starting interval of 1s)
    time elapsed interval
    1s 900ms
    1,9s 729ms
    2,629s 537ms

  10. #10
    Sencha User Animal's Avatar
    Join Date
    Mar 2007
    Location
    Bédoin/Nottingham
    Posts
    30,892

    Default

    Thanks for that. It does start slowly and accelerate, but it starts too slowly, then soon accelerates too quickly. I'd like something that has a nice curve. I'll keep thinking about this. There must be something from "O" level maths that I can remember!

Page 1 of 3 123 LastLast

Similar Threads

  1. Where are animate classes: "Actor" , "Animato
    By Young in forum Ext 2.x: Help & Discussion
    Replies: 13
    Last Post: 25 Jul 2007, 4:00 AM
  2. "FireFox2 kills cursor" and text-select in toolbar
    By brian in forum Ext 1.x: Bugs
    Replies: 8
    Last Post: 7 Jun 2007, 12:12 AM
  3. [1.0a2 Rev 5] Buttons issue ("pressed" state)
    By Arikon in forum Ext 2.x: Help & Discussion
    Replies: 4
    Last Post: 28 Feb 2007, 12:08 PM
  4. A few "yxxxxx" class names still in there.
    By Animal in forum Ext 2.x: Help & Discussion
    Replies: 1
    Last Post: 27 Feb 2007, 1:30 AM
  5. Firebug and enctype="multipart/form-data"
    By Domitian in forum Ext 1.x: Help & Discussion
    Replies: 1
    Last Post: 21 Feb 2007, 2:56 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •