Touch “slide to unlock” in Mobile Safari

Touch “slide to unlock” in Mobile Safari

I've been reading a bit about implementing touch interfaces recently at work. I didn't realize that, for the most part, it's as easy as attaching Javascript touch events to DOM elements - just like you would with any other events in Javascript. The other thing that is a major help with Mobile Safari development is using the new CSS3 properties for animations and such. Thankfully, since you're primarily tagerting Mobile Safari/Webkit, you don't need to worry too much about cross-browser issues.

Last week, Chris Coyier wrote an article over on CSS Tricks about recreating the iphone "slide to unlock" text in Webkit/CSS3. I thought this would be a perfect opportunity to play around and take it one step further to add touch support for iphone and ipad (again, just as an experiment).

The Result

Check out the finished demo on ipad, iphone or ipod touch.

The Markup

The markup is pretty much the same as Chris'. I DID add an id to the arrow slider so that my Javascript would have a DOM hookup.

slider slide to unlock

The CSS

I copied pretty much all of Chris' CSS as well. I did however remove some of the work he did to get the cool animated text highlight since it was messing with some of the Webkit animations I added (and it wasn't really the goal of my experiment). Here is the CSS I've added:

#well {
	-webkit-transition: opacity 0.4s ease-out;
}

This takes care of the fade out animation once you've slid it far enough to unlock. Technically, we'll handle the slide back animation for when you haven't slid the slider far enough in CSS as well, but we'll get to that in the Javascript part.

Here's my final combined CSS:

/*
	 CSS-Tricks Example
	 by Chris Coyier
	 http://css-tricks.com
*/

* { margin: 0; padding: 0; }
html { background: black; }
body { 
	font: 14px Georgia, serif; 
	background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #3b3b3b),color-stop(1, #000000));
	background-repeat: no-repeat;
	min-height: 350px;
	color:#fff;
}
#page-wrap { width: 720px; margin: 0 auto; padding-top: 100px; }

#well {
	padding: 14px 20px 20px 20px;
	-webkit-border-radius: 30px;
	-moz-border-radius: 30px;
	border-radius: 30px;
	
	background: -moz-linear-gradient(top, #010101, #181818);
	background: -webkit-gradient(linear,left top,left bottom,color-stop(0, #010101),color-stop(1, #181818));
	
	border: 2px solid #454545; 
	overflow: hidden;
	
	-webkit-transition: opacity 0.4s ease-out;
}

h2 {
  background: -moz-linear-gradient(left, #4d4d4d, 0.4, #4d4d4d, 0.5, white, 0.6, #4d4d4d, #4d4d4d); 
  background: -webkit-gradient(linear,left top,right top,color-stop(0, #4d4d4d),color-stop(0.4, #4d4d4d),color-stop(0.5, white),color-stop(0.6, #4d4d4d),color-stop(1, #4d4d4d)); 
  
  -moz-background-clip: text;
  -webkit-background-clip: text;
/*  
  -moz-text-fill-color: transparent;
  -webkit-text-fill-color: transparent;
*/  
  -webkit-animation: slidetounlock 5s infinite;
  
  font-size: 80px;
  font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
  font-weight: 300;
  
  padding: 0;
  width: 200%;
}

h2 img {
	vertical-align: middle; 
}

Javascript Fun

Now for the Javascript fun:

function $(id){ return document.getElementById(id); }

$('slider').addEventListener('touchmove', function(event) {
    event.preventDefault();
    var el = event.target;
    var touch = event.touches[0];
    curX = touch.pageX - this.offsetLeft - 73;
    if(curX <= 0) return;
    if(curX > 550){
    	$('well').style.opacity = 0;
    }
   	el.style.webkitTransform = 'translateX(' + curX + 'px)'; 
}, false);

$('slider').addEventListener('touchend', function(event) {	
    this.style.webkitTransition = '-webkit-transform 0.3s ease-in';
    this.addEventListener( 'webkitTransitionEnd', function( event ) { this.style.webkitTransition = 'none'; }, false );
    this.style.webkitTransform = 'translateX(0px)';
}, false);

To capture touch events in Webkit, you'll mostly be working with the touchstart, touchmove and touchend events. After attaching the Javascript touch events, we just have to position the slider button based on the page coordinates of your finger, and trigger our CSS3 animations. I've offset the x-axis position an additional 73px (half of our slider image) to position the touch point for the slider in the middle of the image.

We only neeed to worry about the movement along the x-axis, so we use the translateX function with the -webkit-transform property. The nice thing about Safari is that it will trigger animations automatically when your declared properties are changed - in this case "opacity". These implicit animations also only apply to certain properties.

To get the button to slide back to the starting position when the user releases their finger, we use the touchend event to add the -webkit-transition property to our button. Unfortunately, to avoid funkiness from conflicting events, we need to programmatically apply and remove the transition in the touchend event. We do this by attaching a webkitTransitionEnd event to the object and then setting the CSS property to "none".

Conclusion

This was a fun little experiment to get started using Mobile Safari's touch capabilities. Currently, I've only gotten this working on iphone, ipod touch and ipad. I may play around with trying to get it to work on Android, so if anyone has any suggestions please let me know.

Tags: , , , , ,

Delicious Digg StumbleUpon Reddit

27 Comments

  • Comone
    August 17th, 2010 at 3:46 pm

    Do not working. Copy from CSS Trics blog.

  • Alejandro Vásquez
    August 17th, 2010 at 4:19 pm

    Yeah… posted three days ago on: http://css-tricks.com/slide-to-unlock/

  • Evan
    August 17th, 2010 at 4:38 pm

    Yes, if you read the beginning of the article, I’ve taken Chris’s example on CSS Tricks and taken it a different direction to add touch support in Mobile Safari. It will only work on iPad, iPhone or iPod Touch.

    If you want to see it working on your desktop browser, take a look at his article, since the goal of my experiment was to implement touch support.

  • David
    August 17th, 2010 at 5:34 pm

    Thank you! This is way fun and hopefully I can use that in my projects this year.

  • August 17th, 2010 at 7:48 pm

    Awesome work Evan. I snagged your code and implemented into mine so it’s best of both worlds. Feel free to do the same. I have a feeling I’ll be referencing this a lot as more and more opportunities come up to provide better mobile experiences. I literally didn’t even know there was touch specific JavaScript events.

  • mk
    August 18th, 2010 at 4:56 am

    thanks to the usa copyright policy and apple aggressive use of its predominant position, now this article is illegal. way to go

  • August 19th, 2010 at 11:37 am
  • Evan
    August 19th, 2010 at 4:45 pm

    I’m not implying that you should use this example verbatim in anything that you build or release as your own product for these reasons. It’s only meant as an experiment to recreate Apple’s GUI element.

  • August 21st, 2010 at 8:33 pm

    Awesome article Evan. I didn’t know that it was possible to create touch based with javascript. Thanks for opening way more possibilities. Would it also be possible to create a photo gallery that uses a swipe event to cycle through the images?

  • Evan
    August 24th, 2010 at 3:36 pm

    @John: Thanks. Absolutely. I don’t see why not. Essentially I think you would have some sort of “viewing pane” positioned over some draggable layer that contained your photos.

  • Simith
    September 4th, 2010 at 2:52 pm

    Me and my acquaintance were argument almost an issue similar to this! Instantly I recognize that I was powerful. lol! Thanks for the selective information you position.
    tailored car mats

  • Kirk
    September 11th, 2010 at 5:26 pm

    Great code Evan…

    How could I add a function to redirect to a specific web page once the slider is moved to the end and the arrow fades out?

    I’ve tried to add:

    function(){[removed]=“test.html”}

    with no luck.

    Thx for the blog.

  • October 5th, 2010 at 10:22 pm

    Well done,Great website man…thanks for the post…this article was really great…keep on posting such stuff…great work….

  • February 10th, 2011 at 1:06 am

    Thanks for sharing this whoever said you have copied i don’t mind it but it is useful for other that’s matter….

  • krit
    February 12th, 2011 at 5:36 am

    I tried this and put ur sample into my lockscreen, unfortunately nothing works!

  • Cristofer Robles
    May 2nd, 2011 at 11:22 am

    to redirect to a new page
    put the following code in the following lines

    if (ui.position.left > 550) {
              $(”#well”).fadeOut();
              //[removed].replace(“www.google.cl”);


              [removed] = “http://www.google.com/”


            } else {
              // Apparently Safari isn’t allowing partial opacity on text with background clip? Not sure.
                $(“h2 span”).css(“opacity”, 100 - (ui.position.left / 5))

  • moe
    June 18th, 2011 at 5:04 pm

    can you add the files so we can download them? thanks

  • Carlos
    June 25th, 2011 at 2:36 pm

    How could I add a function to redirect to a specific web page once the slider is moved to the end and the arrow fades out?

    I’ve tried to add:

    $(‘img’).draggable({
      axis: ‘x’,
      containment: ‘parent’,
      drag: function(event, ui) {
          $(‘h2’).css(‘opacity’, 1 - (ui.position.left / 530))
          if (ui.position.left >= 530) {
            $(”#well”).fadeOut().trigger(‘unlocked’);
            [removed] = “http://www.google.com/”


          } else {
              $(“h2 span”).css(“opacity”, 100 - (ui.position.left / 5))
          }
      },
      stop: function(event, ui) {
          if (ui.position.left < 530) {
            var $this = $(this);
            $this.animate({
              left: 0
            }).siblings(‘h2’).animate({
              ‘opacity’: 1
            })
          }
      }
    });

    $(’#well’).bind(‘unlocked’, function(event){
      console.log(‘iPhone slider was unlocked!’)
    })

  • August 23rd, 2011 at 4:28 am

    Thanks for sharing, this article give me a very nice idea about how things are done. Keep up the good work. Regards : Malayalam Film Lover

  • Subhro
    September 21st, 2011 at 10:35 pm

    sir,
        i would be highly obliged if you would suggest me how could I add a function to redirect to a specific web page once the slider is moved to the end and the arrow fades out. and the exact position where to write the code.
    Thank you

  • December 2nd, 2011 at 10:33 am

    Subhro have you found the solution
    I esssayé to add this code


    f (ui.position.left > 550) {
          $(”#well”).fadeOut();
    [removed] = “http://www.google.com/”

    but it does not work
    if anyone can help us
    This will be nice

    thank you for your help

  • mubah
    December 15th, 2011 at 2:34 am

    can i use this one as right to left slide.

  • December 20th, 2011 at 1:03 am

    I still can’t figure out:

    $(‘img’).draggable({
      axis: ‘x’,
      containment: ‘parent’,
      drag: function(event, ui) {

    Let me know if I’m making sense

  • December 20th, 2011 at 4:30 am

    Personne a une idée car l’exemple en haut ne marche pas pour

    Comment pourrais-je ajouter une fonction à une redirection vers une page web spécifique une fois que le curseur est déplacé vers la fin et les fondus flèche sortir?

  • January 19th, 2012 at 12:59 pm

    i labored over this, then found it was very easy. here are the first 10 lines (just change line 9 with the intended url).


    $(function() {

      $(”#slider”).draggable({
          axis: ‘x’,
          containment: ‘parent’,
          drag: function(event, ui) {
            if (ui.position.left > 550) {
              $(”#well”).fadeOut();
              [removed].href = ‘http://www.google.com’;
            } else {

  • January 19th, 2012 at 3:16 pm

    sorry, that was wrong. here is the entire correct script:
    $(function() {

      $(”#slider”).draggable({
          axis: ‘x’,
          containment: ‘parent’,
          drag: function(event, ui) {
            if (ui.position.left > 550) {
              $(”#well”).fadeOut();
            } else {
              // Apparently Safari isn’t allowing partial opacity on text with background clip? Not sure.
              // $(“h2 span”).css(“opacity”, 100 - (ui.position.left / 5))
            }
          },
          stop: function(event, ui) {
            if (ui.position.left < 551) {
              $(this).animate({
                  left: 0
              })
            }
          }
      });
     
      // The following credit: http://www.evanblack.com/blog/touch-slide-to-unlock/
     
      $(’#slider’)[0].addEventListener(‘touchmove’, function(event) {
          event.preventDefault();
          var el = event.target;
          var touch = event.touches[0];
          curX = touch.pageX - this.offsetLeft - 73;
          if(curX <= 0) return;
          if(curX > 550){
            $(’#well’).fadeOut();
                [removed] = “http://www.google.com”; 
          }
            el.style.webkitTransform = ‘translateX(’ + curX + ‘px)’;
      }, false);
     
      $(’#slider’)[0].addEventListener(‘touchend’, function(event) { 
          this.style.webkitTransition = ‘-webkit-transform 0.3s ease-in’;
          this.addEventListener( ‘webkitTransitionEnd’, function( event ) { this.style.webkitTransition = ‘none’; }, false );
          this.style.webkitTransform = ‘translateX(0px)’;
      }, false);

    });

  • Bart
    February 2nd, 2012 at 6:10 am

    Would be a better option instead of translateX

    el.style.webkitTransform = ‘translate3d(’ + curX + ‘px, 0, 0)’;

    be a better option than

    el.style.webkitTransform = ‘translateX(’ + curX + ‘px)’;

    since translate3d uses hardware acceleration

Add your 2 cents:

Remember my personal information

Notify me of follow-up comments?

Please enter the word you see in the image below: