headphone progress knob

by Soviut

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<body ng-app="App">
<div ng-controller="MainCtrl">
    <div class="toolbar">
        <input type="range" name="points" min="0" max="1" step="0.1" ng-model="percent" /> {{ percent * 100 }}%
    </div>
    <headphones width="300" height="300" initial-percent="0" percent="percent" />
</div>
</body>

CSS

.toolbar {
    display: block;
}

headphones {
    display: block;
}

JavaScript

var app = angular.module('App', []);

app.controller('MainCtrl', function($scope) {
    $scope.percent = 0.5;
});

app.directive('headphones', function() {
    return {
        restrict: 'E',
        scope: {
            width: '@',
            height: '@',
            initialPercent: '=',
            percent: '='
        },
        template: '<canvas></canvas>',
        link: function(scope, el, attrs) {
            el.width = scope.width;
            el.height = scope.height;
            
            var canvas = el.children('canvas')[0];
            canvas.width = scope.width;
            canvas.height = scope.height;
            var ctx = canvas.getContext('2d');
            
            var width = canvas.width;
            var height = canvas.height;
            
            var percent = scope.initialPercent;
            var targetPercent = scope.percent;
            var startAngle = Math.PI * 0.64;
            var endAngle = Math.PI * 2.36;
            
            var radius = width * 2;
            var maskWidth = 100;
            var maskHeight = 96;
            var maskScale = Math.min(width / maskWidth, height / maskHeight);
            var centerX = width / 2;
            var centerY = height / 2;
            
            var friction = 0.05;
            var minDiff = 0.0001; // smallest distance before destination is considered reached
            var refreshRate = 16; // milliseconds
            
            var bgColor = '#DDDDDD';
            var fgColor = '#DD0000';
            
            function redraw() {
                var angle = (endAngle - startAngle) * percent + startAngle;
                
                ctx.save();
                ctx.translate((width / 2) - (maskWidth * maskScale / 2), (height / 2) - (maskHeight * maskScale / 2));
                ctx.scale(maskScale, maskScale);
                ctx.beginPath();
                ctx.moveTo(99.4, 52.0);
                ctx.bezierCurveTo(99.5, 50.8, 99.6, 49.6, 99.7, 48.4);
         ...