Prototype a Video Player in Framer

framer_video_player

One of the great things about Framer is that it supports HTML5 video. In this tutorial I’ll show you how to prototype a video player with simple controls inside Framer Studio.

Sample Project

Setting up the video.

To start, let’s get a video playing inside Framer Studio with no controls. Download a short mp4 from Vimeo ‐ I used this one.

Once you have your video, create a new fullscreen Framer Studio project, save it, and place the video file inside the images folder. Then copy paste this code into the editor:

# setup a container to hold everything
videoContainer = new Layer
	width:640
	height:360
	backgroundColor:'#fff'
	shadowBlur:2
	shadowColor:'rgba(0,0,0,0.24)'

# create the video layer
videoLayer = new VideoLayer
	width: 640
	height: 360
	video: "images/your_video.mp4"
	superLayer: videoContainer

# center everything on screen	
videoContainer.center()

Make sure you replace your_video.mp4 with the one you downloaded. You should now see your video centered on screen. Pretty easy so far :)

Next, let’s have our video start and stop on click. All you need for this is a simple conditional statement to check if the video is already paused or playing:

# when the video is clicked
videoLayer.on Events.Click, ->
	# check if the player is paused
	if videoLayer.player.paused == true
		# if true call the play method on the video layer
		videoLayer.player.play()
	else
		# else pause the video
		videoLayer.player.pause()

Now you should be able to start and stop the video on click!

The player.play() and player.pause() methods are part of the HTMLMediaElement Interface. This is a common interface for working with media content in HTML5. You can view the full list of properties and methods here.

Adding basic controls.

Next, let’s add some controls. For a basic player we’re going to need:

  • a play/pause button
  • a mute button
  • a timeline and progress bar
  • a scrubber to move to a specific time

To keep things organized let’s create a container to hold our controls and position it towards the bottom of the video.

# control bar to hold buttons and timeline
controlBar = new Layer
	width:videoLayer.width - 20
	height:48		
	backgroundColor:'rgba(0,0,0,0.75)'	
	clip:false
	borderRadius:'8px'
	superLayer:videoContainer

# center the control bar
controlBar.center()

# position control bar towards the bottom of the video
controlBar.y = videoLayer.maxY - controlBar.height - 10

Once the control bar is in place we can start adding the actual controls. Let’s start with the play, pause, and volume buttons. Copy paste this code right after the last line:

# play button
playButton = new Layer
	width:48
	height:48
	image:'images/play.png'
	superLayer:controlBar

# on/off volume button
volumeButton = new Layer
	width:48
	height:48
	image:'images/volume_on.png'
	superLayer:controlBar

# position the volume button to the right of play
volumeButton.x = playButton.maxX

In the above code we’re adding two image layers as sublayers of the controlBar. For the icons I used Google’s Material Design Icon Set.

Now that we’ve added our buttons, let’s setup the click behavior. For this we’ll need two more click event handlers similar to the one we wrote earlier.

# Function to handle play/pause button
playButton.on Events.Click, ->
	if videoLayer.player.paused == true
		videoLayer.player.play()
		playButton.image = "images/pause.png"
	else
		videoLayer.player.pause()
		playButton.image = "images/play.png"
		
	# simple bounce effect on click	
	playButton.scale = 1.15
	playButton.animate
		properties:
			scale:1
		time:0.1	
		curve:'spring(900,30,0)'	

# Volume on/off toggle
volumeButton.on Events.Click, ->
	if videoLayer.player.muted == false
  		videoLayer.player.muted = true
  		volumeButton.image = "images/volume_off.png"
	else
		videoLayer.player.muted = false
		volumeButton.image = "images/volume_on.png"
		
	# simple bounce effect on click	
	volumeButton.scale = 1.15
	volumeButton.animate
		properties:
			scale:1
		time:0.1	
		curve:'spring(900,30,0)'

You’ll notice that this time we’re also changing the image source for each button depending on whether the conditional is true or false.

We also need to go back in our code and make this happens in the first videoLayer click handler. The videoLayer click event should now look like this:

# when the video is clicked
videoLayer.on Events.Click, ->
	# check if the player is paused
	if videoLayer.player.paused == true
		# if true call the play method on the video layer
		videoLayer.player.play()
		playButton.image = 'images/pause.png'
	else
		# else pause the video
		videoLayer.player.pause()
		playButton.image = 'images/play.png'
		
	# simple bounce effect on click	
	playButton.scale = 1.15
	playButton.animate
		properties:
			scale:1
		time:0.1	
		curve:'spring(900,30,0)'

Adding a timeline and scrubber.

The last thing we need to do is add a timeline to our control bar. The timeline consists of three parts:

  • the timeline
  • a progress bar
  • a scrubber

The code to create these layers looks like this:

# white timeline bar
timeline = new Layer
	width:494
	height:10
	y:volumeButton.midY - 5
	x:volumeButton.maxX + 10
	borderRadius:'10px'
	backgroundColor:'#fff'
	clip:false
	superLayer: controlBar

# progress bar to indicate elapsed time
progress = new Layer
	width:0
	height:timeline.height
	borderRadius:'10px'
	backgroundColor:'#03A9F4'
	superLayer: timeline

# scrubber to change current time
scrubber = new Layer
	width:18
	height:18
	y:-4
	borderRadius:'50%'
	backgroundColor:'#fff'
	shadowBlur:10
	shadowColor:'rgba(0,0,0,0.75)'
	superLayer: timeline

# make scrubber draggable
scrubber.draggable.enabled = true

# limit dragging along x-axis	
scrubber.draggable.speedY = 0

# prevent scrubber from dragging outside of timeline
scrubber.draggable.constraints =
	x:0
	y:timeline.midY
	width:timeline.width
	height:-10

# Disable dragging beyond constraints 
scrubber.draggable.overdrag = false

In the above code we’ve enabled dragging on our scrubber and limited movement along the x-axis by setting the speedY property to zero. We also set the scrubber constraints so dragging is confined to inside the timeline.

The final step is to hook up the progress bar and scrubber so that they update as the video plays, or when the scrubber is moved. Copy and paste the following code into the editor:

# Update the progress bar and scrubber as video plays
videoLayer.player.addEventListener "timeupdate", ->
	# Calculate progress bar position
	newPos = (timeline.width / videoLayer.player.duration) * videoLayer.player.currentTime
	
	# Update progress bar and scrubber
	scrubber.x = newPos
	progress.width = newPos	+ 10

# Pause the video at start of drag
scrubber.on Events.DragStart, ->
	videoLayer.player.pause()

# Update Video Layer to current frame when scrubber is moved
scrubber.on Events.DragMove, ->
	progress.width = scrubber.x + 10

# When finished dragging set currentTime and play video
scrubber.on Events.DragEnd, ->
	newTime = Utils.round(videoLayer.player.duration * (scrubber.x / timeline.width),0);
	videoLayer.player.currentTime = newTime
	videoLayer.player.play()
	playButton.image = "images/pause.png"

As the timeupdate event is fired we calculate the new width of the progress bar based on the currentTime property.

The timeupdate event is fired when the currentTime property changes as part of normal playback.

The last three events handle the dragging behavior for the scrubber. At the start of the drag we pause the video. While the scrubber is being moved the DragMove event updates the progress bar width so that it stays attached to the scrubber. When the scrubber is released the DragEnd event fires and figures out the new time to start playing the video from.

Wrapping up.

If you followed all the steps you should have a basic video player working. Now you can take things further by customizing the controls or adding interactions when the video ends. To detect the end of the video all you need is:

videoLayer.player.on "ended", ->
	print "video ended"	

If you enjoyed this post please share it! For more tutorials like this one Follow me on Twitter.