<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 600" text-rendering="auto" preserveAspectRatio="xMidYMid meet">
<defs>
<circle class="dot" fill="#FF69B4" stroke="none" stroke-width="0" stroke-miterlimit="10" cx="0" cy="300"/>  
  <g class="textGroup">
    <text class='label'>HI</text>
  </g>
<filter id="goo" color-interpolation-filters="sRGB">
        <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" />
        <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 31 -12" result="cm" />

      </filter> 
  <g class="speechBubbleGroup">
	<path class="speechBubbleStroke" fill="none"  stroke-width="14" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
		M69.361,112.063l16.377,28.99l16.562-28.94c37.277-5.125,65.195-27.13,65.195-53.494C167.496,28.456,130.766,4,85.515,4
		C40.275,4,4,28.456,4,58.619c0,26.291,27.361,48.249,65.361,53.448V112.063z"/>
	<path class="speechBubbleFill"  d="M69.361,109.063l16.377,28.99l16.562-28.94
		c37.277-5.125,65.195-27.13,65.195-53.494C167.496,25.456,130.766,1,85.515,1C40.275,1,4,25.456,4,55.619
		c0,26.291,27.361,48.249,65.361,53.448V109.063z"/>
    <text class="iconLabel" x="85" y="67">WONDERFUL!</text>
  </g> 
<g class="popLines" fill="none" stroke="#FF69B4" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10">
	

		<line  x1="107.923" y1="21.37" x2="116.935" y2="4"/>
	
		<line  x1="59.335" y1="24.909" x2="50.5" y2="8.057"/>
	
		<line  x1="21.789" y1="56.11" x2="4" y2="53.67"/>
	
		<line  x1="26.782" y1="98.86" x2="8.057" y2="108.34"/>
	
		<line  x1="65.885" y1="125.86" x2="56.43" y2="145.37"/>
	
		<line  x1="112.429" y1="121.342" x2="121.03" y2="142.564"/>
	
		<line  x1="147.535" y1="93.82" x2="168.155" y2="101.45"/>
	
		<line  x1="149.742" y1="49.01" x2="168.155" y2="42.56"/>
</g>  
<path id="happy" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,8c1.1,0,2,1.3,2,3s-0.9,3-2,3s-2-1.3-2-3
	S20.9,8,22,8z M10,8c1.1,0,2,1.3,2,3s-0.9,3-2,3s-2-1.3-2-3S8.9,8,10,8z M16,28c-5.2,0-9.5-4.4-10-9.9c2.9,1.7,6.4,2.7,10,2.7
	s7.1-1,10-2.7C25.5,23.6,21.2,28,16,28L16,28z"/> 
<path id="cool" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M16,26c-1.5,0-2.9-0.3-4.2-0.9l1-1.7
	c1,0.4,2.1,0.7,3.2,0.7c2.9,0,5.5-1.6,6.9-3.9l1.7,1C22.8,24.1,19.6,26,16,26L16,26z M26,12c0,1.1-0.9,2-2,2h-4c-1.1,0-2-0.9-2-2h-4
	c0,1.1-0.9,2-2,2H8c-1.1,0-2-0.9-2-2V9c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1v1h4V9c0-0.6,0.5-1,1-1h6c0.5,0,1,0.4,1,1V12z"/>  
<path id="evil" d="M32,2c0-1.4-0.3-2.8-0.8-4c-1,2.4-3,4.3-5.5,5.3C23,1.2,19.7,0,16,0S9,1.2,6.3,3.3C3.8,2.3,1.9,0.4,0.8-2
	C0.3-0.8,0,0.6,0,2c0,2.3,0.8,4.4,2.1,6.1C0.8,10.4,0,13.1,0,16c0,8.8,7.2,16,16,16s16-7.2,16-16c0-2.9-0.8-5.6-2.1-7.9
	C31.2,6.4,32,4.3,32,2z M18,11.9c0.1-1.5,1.4-2.5,2.5-3C21.6,8.3,22.7,8,22.8,8c0.5-0.1,1.1,0.2,1.2,0.7s-0.2,1.1-0.7,1.2
	c-0.6,0.1-1.2,0.4-1.8,0.7C21.8,11,22,11.5,22,12c0,1.1-0.9,2-2,2s-2-0.9-2-2C18,12,18,11.9,18,11.9L18,11.9z M8,8.8
	C8.2,8.2,8.7,7.9,9.2,8c0,0,1.1,0.3,2.2,0.8c1.1,0.6,2.5,1.6,2.6,3c0,0,0,0.1,0,0.1c0,1.1-0.9,2-2,2s-2-0.9-2-2c0-0.5,0.2-1,0.5-1.4
	C10,10.4,9.3,10.1,8.8,10C8.2,9.8,7.9,9.3,8,8.8L8,8.8z M16,26c-3.6,0-6.8-1.9-8.6-4.9l2.6-1.5c1.2,2,3.5,3.4,6,3.4s4.8-1.4,6-3.4
	l2.6,1.5C22.8,24.1,19.6,26,16,26z"/>  

<path id="confused" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2
	S20.9,8,22,8z M10,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S8.9,8,10,8z M21.5,25.3c-2.6,0.9-5.5-0.4-6.4-3
	c-0.6-1.6-2.3-2.4-3.8-1.8c-1.4,0.5-2.2,2-1.9,3.5h-2c-0.3-2.3,1-4.5,3.2-5.3c2.6-0.9,5.5,0.4,6.4,3c0.6,1.6,2.3,2.4,3.8,1.8
	c1.4-0.5,2.2-2,1.9-3.5h2C25,22.3,23.7,24.5,21.5,25.3z"/>  
<path id="sad" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2
	S20.9,8,22,8z M10,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S8.9,8,10,8z M22,24.4c-1.2-2-3.5-3.4-6-3.4s-4.8,1.4-6,3.4l-2.6-1.5
	C9.2,19.9,12.4,18,16,18s6.8,1.9,8.6,4.9L22,24.4z"/>  
  <path id="shocked" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M10,14c-1.1,0-2-1.3-2-3s0.9-3,2-3s2,1.3,2,3
	S11.1,14,10,14z M16,26c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S18.2,26,16,26z M22,14c-1.1,0-2-1.3-2-3s0.9-3,2-3s2,1.3,2,3
	S23.1,14,22,14z"/>  
  <path id="wondering" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2
	S20.9,8,22,8z M8,10c0-1.1,0.9-2,2-2s2,0.9,2,2s-0.9,2-2,2S8,11.1,8,10z M10.4,25.2l-0.7-2.4l13.7-4l0.7,2.4L10.4,25.2z"/>  
 <path id="angry" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M18,11.9c0.1-1.5,1.4-2.5,2.5-3
	C21.6,8.3,22.7,8,22.8,8c0.5-0.1,1.1,0.2,1.2,0.7s-0.2,1.1-0.7,1.2c-0.6,0.1-1.2,0.4-1.8,0.7C21.8,11,22,11.5,22,12c0,1.1-0.9,2-2,2
	s-2-0.9-2-2C18,12,18,11.9,18,11.9L18,11.9z M8,8.8C8.2,8.2,8.7,7.9,9.2,8c0,0,1.1,0.3,2.2,0.8c1.1,0.6,2.5,1.6,2.6,3
	c0,0,0,0.1,0,0.1c0,1.1-0.9,2-2,2s-2-0.9-2-2c0-0.5,0.2-1,0.5-1.4C10,10.4,9.3,10.1,8.8,10C8.2,9.8,7.9,9.3,8,8.8L8,8.8z M22,24.4
	c-1.2-2-3.5-3.4-6-3.4s-4.8,1.4-6,3.4l-2.6-1.5C9.2,19.9,12.4,18,16,18s6.8,1.9,8.6,4.9L22,24.4z"/>  
<path id="baffled" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M8,13c0-1.7,1.3-3,3-3s3,1.3,3,3s-1.3,3-3,3
	S8,14.7,8,13z M20,24h-8v-2h8V24z M21,16c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S22.7,16,21,16z M12,13c0,0.6-0.4,1-1,1s-1-0.4-1-1
	s0.4-1,1-1S12,12.4,12,13z M22,13c0,0.6-0.4,1-1,1s-1-0.4-1-1s0.4-1,1-1S22,12.4,22,13z"/>  
<path id="smile" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2
	S20.9,8,22,8z M10,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S8.9,8,10,8z M16,26c-3.6,0-6.8-1.9-8.6-4.9l2.6-1.5c1.2,2,3.5,3.4,6,3.4
	s4.8-1.4,6-3.4l2.6,1.5C22.8,24.1,19.6,26,16,26z"/>  
<path id="sleepy" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M9.7,10.3c-0.4,0.4-1,0.4-1.4,0s-0.4-1,0-1.4
	c1.4-1.4,4-1.4,5.4,0c0.4,0.4,0.4,1,0,1.4c-0.2,0.2-0.5,0.3-0.7,0.3s-0.5-0.1-0.7-0.3C11.7,9.7,10.3,9.7,9.7,10.3z M16,26
	c-2.2,0-4-2.2-4-5s1.8-5,4-5s4,2.2,4,5S18.2,26,16,26z M23.7,10.3c-0.2,0.2-0.5,0.3-0.7,0.3s-0.5-0.1-0.7-0.3c-0.6-0.6-2-0.6-2.6,0
	c-0.4,0.4-1,0.4-1.4,0s-0.4-1,0-1.4c1.4-1.4,4-1.4,5.4,0C24.1,9.3,24.1,9.9,23.7,10.3z"/>  
  <path id="tongue" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M10,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2
	S8.9,8,10,8z M24,20h-2v3c0,1.7-1.3,3-3,3s-3-1.3-3-3v-3H8v-2h16V20z M22,12c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S23.1,12,22,12z"/>   
  <path id="grin" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M22,7.4c2,0,3.6,1.6,3.6,3.6
	c0,0.2,0,0.4-0.1,0.6c-0.1,0.3-0.3,0.5-0.6,0.5s-0.6-0.2-0.6-0.5c-0.2-1.1-1.2-1.7-2.3-1.7s-2.1,0.5-2.3,1.7c0,0.3-0.3,0.5-0.6,0.5
	l0,0c-0.3,0-0.6-0.2-0.6-0.5c0-0.2-0.1-0.4-0.1-0.6C18.4,9,20,7.4,22,7.4L22,7.4z M10,7.4c2,0,3.6,1.6,3.6,3.6c0,0.2,0,0.4-0.1,0.6
	c-0.1,0.3-0.3,0.5-0.6,0.5s-0.6-0.2-0.6-0.5c-0.2-1.1-1.2-1.7-2.3-1.7s-2.1,0.5-2.3,1.7c-0.1,0.3-0.3,0.5-0.6,0.5l0,0
	c-0.3,0-0.6-0.2-0.6-0.5c0-0.2-0.1-0.4-0.1-0.6C6.4,9,8,7.4,10,7.4L10,7.4z M6,18h6v7.7C8.6,24.9,6,21.7,6,18z M14,26v-8h4v8H14z
	 M20,25.7V18h6C26,21.7,23.4,24.9,20,25.7z"/>  
  <path id="neutral" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M20,24h-8v-2h8V24z M22,8c1.1,0,2,0.9,2,2
	s-0.9,2-2,2s-2-0.9-2-2S20.9,8,22,8z M10,8c1.1,0,2,0.9,2,2s-0.9,2-2,2s-2-0.9-2-2S8.9,8,10,8z"/>  
  
</defs>
  <g class="dotGroup">
<g class="dotContainer" filter="url(#goo)" >
     <rect class="hitArea"/>  
    </g>

    <g class="iconContainer"/>
    <g class="bubbleContainer"/>
 </g>
  <g class="shine" opacity="0">
<!-- <circle  fill="rgba(72, 139, 218, 0)" stroke="#C6FF69" stroke-width="0" stroke-miterlimit="10" cx="400" cy="300" r="50"/> -->
  <ellipse id="shineM"  fill="#FFF" opacity="0.12" stroke="none" stroke-width="0" stroke-miterlimit="10" cx="399" cy="277" rx="20" ry="12"/>
    <ellipse id="shineL"  fill="#FFF" opacity="0.12" stroke="none" stroke-width="0" stroke-miterlimit="10" cx="420" cy="277" rx="10" ry="5"/>      
    <ellipse id="shineR"  fill="#FFF" opacity="0.12" stroke="none" stroke-width="0" stroke-miterlimit="10" cx="420" cy="277" rx="10" ry="5"/>  
  </g>
</svg>
  
body {
  background-color:#2d2d2d;
  overflow: hidden;
  font-family: 'Passion One', sans-serif;
  
}

body,
html {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  -webkit-tap-highlight-color: rgba(0,0,0,0); 
  text-align:center
}


svg{
  width:100%;
  height:100%;
  visibility:hidden;
/*  overflow:visible; */
}
.shine, .iconContainer{
  pointer-events:none;
}
.dot{
  cursor:pointer;
}

.shine circle{
  stroke:#FFF;
}

.speechBubbleGroup text{
  text-anchor:middle;
  font-size:29px;
  
}

line{
  vector-effect:"non-scaling-stroke";
}

.hitArea, .dragContainer{
  -webkit-tap-highlight-color: rgba(0,0,0,0);
}
console.clear();
var xmlns = "http://www.w3.org/2000/svg",
  xlinkns = "http://www.w3.org/1999/xlink",
  select = function(s) {
    return document.querySelector(s);
  },
  selectAll = function(s) {
    return document.querySelectorAll(s);
  },
  //container = select('.container'),
  dotContainer = select('.dotContainer'),
  iconContainer = select('.iconContainer'),
  bubbleContainer = select('.bubbleContainer'),
  hitArea = select('.hitArea'),
  dotGroup = select('.dotGroup'),
  spacer = 60,
  minDragX,
  numItems = 20,
  dotSize = 10,
  //step, = spacer + (dotSize / 2),
  snapArray = [],
  multiplier = 4.8,
  iconArray = [  'cool','evil', 'confused', 'sad', 'shocked', 'smile', 'wondering', 'happy','angry', 'baffled', 'sleepy', 'tongue', 'grin', 'neutral'],
  numItems = iconArray.length,
  currentIconId,
  oldIconId = -1,
  currentSpeechBubble,
  dragger, 
  uiColor = '#FF5EAE',
  textColor = '#FF5EAE',
  iconColor = '#FFF',
  bubbleFillColor = '#FFF'   

TweenMax.set('svg', {
  visibility: 'visible'
})

//set colour
TweenMax.set(['line'], {
  stroke:uiColor
})

TweenMax.set(['.speechBubbleFill'], {
  fill:bubbleFillColor
})
TweenMax.set(['.speechBubbleStroke'], {
  stroke:uiColor
})

TweenMax.set(['.dot'], {
  fill:uiColor
})


TweenMax.set([ '.iconLabel'], {
  fill:textColor
})

var mainTl = new TimelineMax({
  paused: true
});

function makeMenu() {
  var tl;
  for (var i = 0; i < numItems; i++) {
    var c = select('.dot').cloneNode(true);
    var ic = select('#' +iconArray[i]).cloneNode(true);
    ic.setAttribute('class', 'icon');
    dotContainer.appendChild(c);
    iconContainer.appendChild(ic);
    c.setAttributeNS(null, 'btnId', i);
    TweenMax.set(c, {
      attr: {
        cx: (i * spacer),
        r: dotSize
      }
    })
    TweenMax.set(ic, {
        x: (i * spacer) - 16,
        y:300 - 16,
        width:0,
        height:0,
        transformOrigin:'50% 50%',
        scale:0,
        alpha:0,
        fill:iconColor
    })

    snapArray.push((-i * (spacer)));

    tl = new TimelineMax({});

    tl.to(c, 1, {
        attr: {
          r: dotSize * multiplier
        },
        ease: Linear.easeNone
      })
      .to(ic, 1, {
        alpha: 1,
        scale: 2,
        ease: Linear.easeNone

      }, '-=1')
      .to(c, 1, {
        attr: {
          r: dotSize 
        },
        ease: Linear.easeNone      
      })
      .to(ic, 1, {
        alpha: 0,
        scale: 0,
        ease: Linear.easeNone
      }, '-=1')

    mainTl.add(tl, (i / 2));

  }

  minDragX = (-(numItems - 1) * spacer);

  

  dragger = Draggable.create(dotContainer, {
    type: 'x',
    bounds: {
      minX: minDragX,
      maxX: 0
    },
    onDrag: dragSlider,
    onDragStart: dragStart,
    onThrowUpdate: dragSlider,
    throwProps: true,
    onThrowComplete: throwComplete,
    minDuration: 1,
    snap: snapArray,
    overshootTolerance:0,
    dragClickables:true
  })
  
  dragger[0].disable();
  

  TweenMax.set(dotGroup, {
    x: 400
  })

  TweenMax.set(hitArea, {
    width: dotContainer.getBBox().width,
    height: dotSize * multiplier * 2,
    x: dotContainer._gsTransform.x,
    y: select('.dot').getAttribute('cy') - ((dotSize * multiplier)),
    fill: 'transparent'
  })

  TweenMax.to([dotContainer, iconContainer], 2, {
    x: (snapArray[Math.floor(numItems / 2)]),
    onUpdate: dragSlider,
    onComplete: function(){
      throwComplete();
      dragger[0].enable();

    },
    ease:Elastic.easeOut.config(1, 0.85)
  })

  createSpeechBubble();
} //end function


function dragSlider() {
  var posX = dotContainer._gsTransform.x;
  //console.log(posX)
  TweenMax.to(mainTl, 0.5, {
      //time:((posX/minDragX) * (mainTl.duration()-2))+1,
      time: ((posX / minDragX) * (mainTl.duration() - 2)) + 1,
      ease: Elastic.easeOut.config(2, 0.75)
    })
  TweenMax.set(iconContainer, {
    x: posX
  })  
  TweenMax.to( bubbleContainer,1, {
    x: posX,
    ease:Elastic.easeOut.config(1, 0.5)
  })
}

function throwComplete() {
  
  
  var landed = Math.ceil(dotContainer._gsTransform.x / spacer);
  currentIconId = Math.abs(landed);

  showSpeechBubble();


}

function createSpeechBubble(){
 currentSpeechBubble = select('.speechBubbleGroup').cloneNode(true);
  bubbleContainer.appendChild(currentSpeechBubble); 
  TweenMax.set(currentSpeechBubble, {
    y:80,
    scale:0,
     transformOrigin:'50% 100%'
  })  
}
function showSpeechBubble(){  
  
  currentSpeechBubble.querySelector('text').textContent = iconArray[currentIconId].toUpperCase();
  var tl = new TimelineMax();
  tl.set(currentSpeechBubble, {
    x:currentIconId * spacer - (currentSpeechBubble.getBBox().width/2) - 5,
    rotation:(oldIconId < currentIconId) ? 45 : -45
  })
  .to(currentSpeechBubble, 1, {
    rotation:0,
    ease:Elastic.easeOut.config(1, 0.6),
    scaleX:1
  })
    .to(currentSpeechBubble, 0.6, {
    ease:Elastic.easeOut.config(1, 0.6),
    //alpha:1,
    scaleY:1
  },'-=1')
  
}

function clearSpeechBubble(){

  var pl = select('.popLines').cloneNode(true);
  
  bubbleContainer.appendChild(pl);
  var tl = new TimelineMax({onComplete:function(){
    if(this.data.lines){
      bubbleContainer.removeChild(this.data.lines);
    }      
    }});
  tl.data = {lines:pl};
  tl.set(pl, {
    x:currentSpeechBubble._gsTransform.x,
    y:currentSpeechBubble._gsTransform.y,
    transformOrigin:'50% 50%',
    scale:0.8
  })
    .to(pl.querySelectorAll('line'), 0.3, {
    drawSVG:'100% 100%',    
    ease:Linear.easeNone
  })
    .to(pl, 0.3, {
      scale:1.4,
      ease:Expo.easeOut
    
  },'-=0.3')
  
    TweenMax.set(currentSpeechBubble, {
      scale:0
    })
  

  
}

function dragStart(){
  
  clearSpeechBubble()
}

document.body.onclick = function(e){
  if(e.target.className.baseVal !== 'dot'){
    return;
  }

  //console.log(e.target.getAttribute('btnId'));
  oldIconId = currentIconId;
  currentIconId = parseInt(e.target.getAttribute('btnId'));
  
  if(oldIconId == currentIconId){
    
    return
  }
    clearSpeechBubble();
    TweenMax.to([dotContainer, iconContainer], 0.8, {
      x: (snapArray[currentIconId]),
      onUpdate: dragSlider,
      onComplete: throwComplete,
      ease:Power1.easeOut
  })
    
    
}

makeMenu();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/TweenMax-latest-beta.js
  2. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/utils/Draggable.min.js
  3. //s3-us-west-2.amazonaws.com/s.cdpn.io/16327/ThrowPropsPlugin.min.js
  4. //s3-us-west-2.amazonaws.com/s.cdpn.io/16327/DrawSVGPlugin.js?r=12