Skip to content

Instantly share code, notes, and snippets.

@timruffles
Created April 23, 2015 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timruffles/bfbc53c5d7444a506470 to your computer and use it in GitHub Desktop.
Save timruffles/bfbc53c5d7444a506470 to your computer and use it in GitHub Desktop.
source code for demonstrating using ngModelController with SVG and Angular - full post https://truffles.me.uk/turn-anything-into-an-ng-model-with-ngmodelcontroller
"use strict";
/*
the `clock()` directive the only bit of the code that's handling ngModel - the
rest is basic angular or DOM code
*/
function clockDirective() {
return {
// we require an instance of ng-model to work
require: "ngModel",
link: function(scope, el, attrs, ngModel) {
// initialize the non-angular widget, and
// pass in our model updating fn
var clock = donutClock({
onInput: function view2model(fromView) {
ngModel.$setViewValue(fromView);
},
el: el[0],
});
// when angular detects a change to the model,
// we update our widget
ngModel.$render = function model2view(time) {
clock.set(ngModel.$viewValue);
}
}
}
}
function TimeCtrl($scope, $interpolate, $interval) {
var MINUTE = 60 * 1000;
var HOUR = 60 * MINUTE;
var DAY = 24 * HOUR;
var CIRCLE = Math.PI * 2;
var COLOUR_TIME_OFFSET = Math.PI / 2;
$scope.item = { time: new Date };
var ticker;
var h = 201;
var s = 80;
var hsl = $interpolate("hsl({{h}}, {{s}}%, {{l}}%)");
$scope.timeToColour = function(time) {
var ratio = Math.sin((time / DAY) * CIRCLE - COLOUR_TIME_OFFSET);
var l = 20 + (70 * ratio);
return hsl({h:h,s:s,l:l});
};
Object.defineProperty($scope, "ticking", {
get: function() {
return !!ticker;
}
});
$scope.toggle = function() {
if(ticker) {
$interval.cancel(ticker);
ticker = null;
} else {
ticker = $interval(function() {
$scope.item.time = new Date(+$scope.item.time + 5 * MINUTE);
}, 16);
}
}
}
function donutClock(options) {
var CIRCLE = Math.PI * 2;
var DAY = 24 * 60 * 60 * 1000;
assert(typeof options === "object", "pass object of options");
options.change = options.change || Function.prototype;
var pointerHandlerR = 12.5;
var el = assert(options.el instanceof Element, "missing element") && options.el;
template(el);
var pointerHander = qs(el, "#hand-for-pointer");
attr(pointerHander,"r", pointerHandlerR);
var face = el.querySelector("#face");
attr(face,"r",150)("cx",150)("cy",150);
var removeMask = qs(el, "#remove-centre circle");
attr(removeMask, "cx", 150)("cy", 150)("r", 125);
var r = +attr(face, "r");
var handCircuitR = r - pointerHandlerR;
pointerHander.addEventListener("mousedown", mousedown);
return {
set: function(v) {
renderAngle(timeToAngle(v));
}
};
function qs(el, css) {
return el.querySelector(css);
}
// silly dom tool ;)
function attr(el, k, v) {
if(v == null)
return el.getAttribute(k)
else {
el.setAttribute(k,v);
return attr.bind(null, el);
}
}
// event handlers
function mousedown() {
document.body.addEventListener("mousemove", update);
document.body.addEventListener("mouseup", disable);
}
function disable() {
document.body.removeEventListener("mousemove", update);
document.body.removeEventListener("mouseup", disable);
}
// conversion
function angleToTime(angle) {
var ratio = angle / CIRCLE;
ratio = ratio + 0.25;
ratio = ratio < 0 ? 1 + ratio : ratio;
return ratio * DAY;
}
function timeToAngle(time) {
var ratio = timeOnly(time) / DAY;
if(ratio >= 0.75) {
ratio = -(1 - ratio);
}
ratio -= 0.25;
ratio *= CIRCLE;
return ratio;
}
function timeOnly(date) {
var mid = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
return date - mid;
}
function update(event) {
var rect = el.getBoundingClientRect();
var diffX = event.clientX - (rect.left + rect.width / 2);
var diffY = event.clientY - (rect.top + rect.height / 2);
var angle = Math.atan2(diffY, diffX);
options.onInput( new Date(angleToTime(angle)) );
renderAngle(angle);
}
function renderAngle(angle) {
var x = r + Math.cos(angle) * handCircuitR;
var y = r + Math.sin(angle) * handCircuitR;
pointerHander.setAttribute("cx", x);
pointerHander.setAttribute("cy", y);
}
function assert(t, msg) {
if(!t) throw new Error(msg);
return t;
}
function svgEl(name) {
return document.createElementNS("http://www.w3.org/2000/svg", name);
}
function template(root) {
var defs = svgEl("defs");
var mask = svgEl("mask");
mask.id = "remove-centre";
var rect = svgEl("rect");
attr(rect, "width", "100%")
("height", "100%")
("fill", "#fff");
mask.appendChild(rect);
mask.appendChild(svgEl("circle"));
defs.appendChild(mask);
var face = svgEl("circle");
face.id = "face";
attr(face, "mask", "url(#remove-centre)");
var hand = svgEl("circle");
hand.id = "hand-for-pointer";
root.appendChild(defs);
root.appendChild(face);
root.appendChild(hand);
}
}
angular.module("demo", [])
.directive("clockInput", clockDirective)
.controller("TimeCtrl", TimeCtrl);
angular.bootstrap(document.documentElement, ["demo"]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment