最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to implement *object* for improve my clock sample javascript program - Stack Overflow

programmeradmin0浏览0评论

The goal of this work is to understand and play with meaning of some object concept I've heard arround.

About the bounty

There is a lot of different way / approach to do this.

My tries are not really clean: for adding a 2st clock, with another timezone, I have to edit 3 different places. This is not well (see at bottom of the answer).

How could I do something more useful?

In the begining:

post-edit: the initial question was about choosing between jquery and mootools, now choice as been made; the goal is to improve this, with the use of mootools.

There is a little sample/demo i wrote to play with javascript and svg:

var cx  =128;
var cy  =128;
var slen=120;
var mlen=116;
var hlen= 80;
var selem;
var melem;
var helem;
function setvars() {
    selem=document.getElementById("seconds");
    melem=document.getElementById("minutes");
    helem=document.getElementById("hours");
    drawtime();
};
function drawtime() {
    var now=new Date();
    var nows=now.getTime()%60000;
    var nowm=now.getMinutes()*1.0+1.0*nows/60000;
    var nowh=now.getHours()*1.0+1.0*nowm/60;
    var sposx=cx + slen * Math.sin( nows / 30000 * Math.PI );
    var sposy=cy - slen * Math.cos( nows / 30000 * Math.PI );
    var mposx=cx + mlen * Math.sin( nowm / 30 * Math.PI );
    var mposy=cy - mlen * Math.cos( nowm / 30 * Math.PI );
    var hposx=cx + hlen * Math.sin( nowh / 6 * Math.PI );
    var hposy=cy - hlen * Math.cos( nowh / 6 * Math.PI );
    selem.setAttribute("x1",sposx);
    selem.setAttribute("y1",sposy);
    selem.setAttribute("x2",sposx);
    selem.setAttribute("y2",sposy);
    melem.setAttribute("x2",mposx);
    melem.setAttribute("y2",mposy);
    helem.setAttribute("x2",hposx);
    helem.setAttribute("y2",hposy);
    window.setTimeout(drawtime,80)
};
setvars();
#box1    { stroke: black; }
#minutes { stroke: #2266AA; }
#hours   { stroke: #3388CC; }
#seconds { stroke: #CCCC22; }
line,circle {
    opacity:0.65;
    fill:none;
    stroke-width:8;
    stroke-linecap:round;
    stroke-linejoin:round;
    marker:none;
    stroke-miterlimit:4;
    stroke-dasharray:none;
    stroke-opacity:1;
    visibility:visible;
    display:inline;
    overflow:visible;
    enable-background:accumulate
}
<svg xmlns="" id="svg2" width="100%"
     height="100%" viewBox="0 0 900 256" version="1.0">
    <title  id="title1">Clock</title>
    <circle id="box1"    cy="128" cx="128"  r="124" />
    <line   id="hours"   x1="128" y1="128" x2="128"  y2="48" />
    <line   id="minutes" x1="128" y1="128" x2="244" y2="128" />
    <line   id="seconds" x1="128"   y1="8" x2="128"   y2="8" />
</svg>

The goal of this work is to understand and play with meaning of some object concept I've heard arround.

About the bounty

There is a lot of different way / approach to do this.

My tries are not really clean: for adding a 2st clock, with another timezone, I have to edit 3 different places. This is not well (see at bottom of the answer).

How could I do something more useful?

In the begining:

post-edit: the initial question was about choosing between jquery and mootools, now choice as been made; the goal is to improve this, with the use of mootools.

There is a little sample/demo i wrote to play with javascript and svg:

var cx  =128;
var cy  =128;
var slen=120;
var mlen=116;
var hlen= 80;
var selem;
var melem;
var helem;
function setvars() {
    selem=document.getElementById("seconds");
    melem=document.getElementById("minutes");
    helem=document.getElementById("hours");
    drawtime();
};
function drawtime() {
    var now=new Date();
    var nows=now.getTime()%60000;
    var nowm=now.getMinutes()*1.0+1.0*nows/60000;
    var nowh=now.getHours()*1.0+1.0*nowm/60;
    var sposx=cx + slen * Math.sin( nows / 30000 * Math.PI );
    var sposy=cy - slen * Math.cos( nows / 30000 * Math.PI );
    var mposx=cx + mlen * Math.sin( nowm / 30 * Math.PI );
    var mposy=cy - mlen * Math.cos( nowm / 30 * Math.PI );
    var hposx=cx + hlen * Math.sin( nowh / 6 * Math.PI );
    var hposy=cy - hlen * Math.cos( nowh / 6 * Math.PI );
    selem.setAttribute("x1",sposx);
    selem.setAttribute("y1",sposy);
    selem.setAttribute("x2",sposx);
    selem.setAttribute("y2",sposy);
    melem.setAttribute("x2",mposx);
    melem.setAttribute("y2",mposy);
    helem.setAttribute("x2",hposx);
    helem.setAttribute("y2",hposy);
    window.setTimeout(drawtime,80)
};
setvars();
#box1    { stroke: black; }
#minutes { stroke: #2266AA; }
#hours   { stroke: #3388CC; }
#seconds { stroke: #CCCC22; }
line,circle {
    opacity:0.65;
    fill:none;
    stroke-width:8;
    stroke-linecap:round;
    stroke-linejoin:round;
    marker:none;
    stroke-miterlimit:4;
    stroke-dasharray:none;
    stroke-opacity:1;
    visibility:visible;
    display:inline;
    overflow:visible;
    enable-background:accumulate
}
<svg xmlns="http://www.w3/2000/svg" id="svg2" width="100%"
     height="100%" viewBox="0 0 900 256" version="1.0">
    <title  id="title1">Clock</title>
    <circle id="box1"    cy="128" cx="128"  r="124" />
    <line   id="hours"   x1="128" y1="128" x2="128"  y2="48" />
    <line   id="minutes" x1="128" y1="128" x2="244" y2="128" />
    <line   id="seconds" x1="128"   y1="8" x2="128"   y2="8" />
</svg>

(Originaly posted at jsfiddle) as I'm not very experienced with javascript jquery and/or mootools, I would like to know if some simplier methods exists, maybe in writting this in a different manner.

how to do simple rotation about a fixed center, using jquery or mootools:

var hposx=cx + hlen * Math.sin( nowh / 6 * Math.PI );
var hposy=cy - hlen * Math.cos( nowh / 6 * Math.PI );
helem.setAttribute("x2",hposx);
helem.setAttribute("y2",hposy);

how to objectize this code? (if even it could be a good thing)...

All samples using object orientation, specific library, or else are wele!

Share Improve this question edited May 23, 2017 at 12:10 CommunityBot 11 silver badge asked Nov 3, 2012 at 10:42 F. Hauri - Give Up GitHubF. Hauri - Give Up GitHub 71.4k19 gold badges131 silver badges148 bronze badges 6
  • 3 Your question is not very clear. Could you please elaborate on what objectize means? – user1726343 Commented Nov 3, 2012 at 10:47
  • Code looks fairly streamlined to me. – Jeffrey Sweeney Commented Nov 3, 2012 at 11:42
  • Rather than edit all meaning out of your original question, which also renders answers disconnected. Please post your solution below. – Sparky Commented Nov 4, 2012 at 13:49
  • instead of mootools, I suggest using d3.js to make a clock with moving hands – Dave Alperovich Commented May 19, 2015 at 17:53
  • @DaveAlperovich I'm ok with that, my personal inlab try is now pure javascript again... But care, transform, rotate and translate exist in SVG and/or in CSS! – F. Hauri - Give Up GitHub Commented May 19, 2015 at 19:24
 |  Show 1 more ment

4 Answers 4

Reset to default 4

Your code is simple and straightforward. I don't think you should try using jQuery or MooTools if your task is simple without it.

For rotation I don't think there are built in tools in jQuery or MooTools, but there are matrix transformations you can use on svg objects, read: http://msdn.microsoft./en-us/library/ie/hh535760%28v=vs.85%29.aspx

Or check this question: SVG rotate transform Matrix .

As for making an object:

You can of course slice up your code to more functions, or you make an object that represents the current hours/minutes/secs

var clock = {
    time: {
        s: 0,
        m: 0,
        h: 0    
    },
    pos: {
        x: 128,
        y: 128
    },

   .... anything else you might want to add
};

You set its properties first in a set of functions

clock.setTime = function (date) {
    this.time.s = date.getTime()%60000;
    this.time.m = date.getMinutes()*1.0+1.0*nows/60000;
    this.time.h = date.getHours()*1.0+1.0*nowm/60;
};

And read them in an other set of functions:

clock.getMinPos = function () {
    var x = ...;// Sine is ok.
    var y = ...;// Cosine is ok.
    // I don't like matrices anyway.
    return [x, y];
};

Really just try to partition your code into functional tasks. One function should only do one thing.

I've created a D3 clock Object

Plunker


:

  • d3Clock Can be instantiated multiple times.

  • A constructor takes X, Y, Width, TimeZoneOffset Object, label

:

  <script>
    $(document).ready(function() {

      var tokyoClock = new d3Clock({
        clockCenterX: 140,
        clockCenterY: 150,
        clockWidth: 130,
        title: 'Tokyo',
        TZOffset: {
          hours: 13
        }
      });

:

  • Methods are also expose to manually set TZOffsets

:

      var iranClock = new d3Clock({
        clockCenterX: 355,
        clockCenterY: 360,
        clockWidth: 180,
        title: 'Iran'    
      });

      iranClock.Hours(8);
      iranClock.Minutes(30);

The d3Clock Enclosure:

var d3Clock = function(data) {
  var clockGroup, fields, formatHour, formatMinute, formatSecond, pi, render, scaleHours, scaleSecsMins, vis;

  width = data.clockWidth;
  height = data.clockWidth;
  offSetX = data.clockWidth / 2;
  offSetY = offSetX;
  var x = data.clockCenterX;
  var y = data.clockCenterY;

  var hourOffset = 0;
  var minOffset = 0;
  var secOffset = 0;

  title = data.title;

  var clockRadius = data.clockWidth * 0.45;
  formatSecond = d3.time.format("%S");
  formatMinute = d3.time.format("%M");
  formatHour = d3.time.format("%H");

  pi = Math.PI;
  scaleSecsMins = d3.scale.linear().domain([0, 59 + 59 / 60]).range([0, 2 * pi]);
  scaleHours = d3.scale.linear().domain([0, 11 + 59 / 60]).range([0, 2 * pi]);

  // create Parent Div and set x, y
  var iDiv = document.createElement('div');
  iDiv.setAttribute("style", 'background-color: transparent; height: ' + width + ' px; width: ' + width + 'px; position: absolute; top:' + y + 'px; left: ' + x + 'px; text-align: center; v-align:bottom;');
  iDiv.innerHTML = '<span style="font-weight: bold;">' + title + '</span>';
  document.getElementsByTagName('body')[0].appendChild(iDiv);
  vis = d3.select(iDiv).append("svg:svg").attr("width", width).attr("height", height);

  clockGroup = vis.append("svg:g").attr("transform", "translate(" + offSetX + "," + offSetY + ")");
  clockGroup.append("svg:circle").attr("r", clockRadius).attr("fill", "lightgrey").attr("class", "clock outercircle").attr("stroke", "black").attr("stroke-width", 5);
  clockGroup.append("svg:circle").attr("r", 4).attr("fill", "black").attr("class", "clock innercircle");


  // private set TZ Offset methods
  var setTZSeconds = function(sec) {
    if (sec !== undefined)
      secOffset = sec;
  }
  var setTZMins = function(min) {
    if (min !== undefined)
      minOffset = min;
  }
  var setTZHours = function(hr) {
    if (hr !== undefined)
      hourOffset = hr;
  }


  // exposed TimeZoneOffset
    this.Seconds = function(Sec) {
      setTZSeconds(Sec);
    }
    this.Minutes = function(Mins) {
      setTZMins(Mins);
    }
    this.Hours = function(Hours) {
      setTZHours(Hours);
    }


  if (data.TZOffset != undefined) {
    setTZHours(data.TZOffset.hours);
    setTZMins(data.TZOffset.mins);
    setTZSeconds(data.TZOffset.secs);
  }

  // Get time values and apply offsets
  fields = function() {
    var d, data, hour, minute, second;
    d = new Date();
    second = d.getSeconds() + secOffset;
    minute = d.getMinutes() + minOffset;
    hour = d.getHours() + hourOffset + minute / 60;
    return data = [{
      "unit": "seconds",
      "text": formatSecond(d),
      "numeric": second
    }, {
      "unit": "minutes",
      "text": formatMinute(d),
      "numeric": minute
    }, {
      "unit": "hours",
      "text": formatHour(d),
      "numeric": hour
    }];
  };


  render = function(data) {
    var hourArc, minuteArc, secondArc;
    clockGroup.selectAll(".clockhand").remove();
    secondArc = d3.svg.arc().innerRadius(0).outerRadius(clockRadius * .9).startAngle(function(d) {
      return scaleSecsMins(d.numeric);
    }).endAngle(function(d) {
      return scaleSecsMins(d.numeric);
    });
    minuteArc = d3.svg.arc().innerRadius(0).outerRadius(clockRadius * .85).startAngle(function(d) {
      return scaleSecsMins(d.numeric);
    }).endAngle(function(d) {
      return scaleSecsMins(d.numeric);
    });
    hourArc = d3.svg.arc().innerRadius(0).outerRadius(clockRadius * .7).startAngle(function(d) {
      return scaleHours(d.numeric % 12);
    }).endAngle(function(d) {
      return scaleHours(d.numeric % 12);
    });
    clockGroup.selectAll(".clockhand").data(data).enter().append("svg:path").attr("d", function(d) {
      if (d.unit === "seconds") {
        return secondArc(d);
      } else if (d.unit === "minutes") {
        return minuteArc(d);
      } else if (d.unit === "hours") {
        return hourArc(d);
      }
    }).attr("class", "clockhand").attr("stroke", "black").attr("style", function(d) {
      if (d.unit === "seconds") {
        return "stroke-width: 2; stroke: white"
      } else if (d.unit === "minutes") {
        return "stroke-width: 3;"
      } else if (d.unit === "hours") {
        return "stroke-width: 4;"
      }
    }).attr("fill", "none");
  };

  setInterval(function() {
    var data;
    data = fields();
    return render(data);
  }, 1000);
};

More methods can be exposed to allow customization

all functions with prefix this. are exposed public to be used on instantiated object. So:

this.Seconds = function(Sec) {
  setTZSeconds(Sec);
}
this.Minutes = function(Mins) {
  setTZMins(Mins);
}
this.Hours = function(Hours) {
  setTZHours(Hours);
}

are exposed access to the private setTZSeconds(), setTZMins(), setTZHours().

It's considered good OO Form for public methods to call private methods that do the work. The public methods can limit or filter the results (like I check not undefined in the private methods.

Public methods can be exposed to

  • change x, y so the clockObjects can be moved around the screen after instantiation
  • change size
  • change styles for clock hands, title location / styling

Edited from bounty: Go to bottom of this!

First answer

After reading first ments from pete (many thanks! you give me good ways of research) and SoonDead (thanks too for the first step of conversion), I've a little look around, jquery and mootools code and finally i've choose mootools to reduce my code, as mootools let me submit an array for object.get and a hash (associative array) for objet.set:

(function () {
    "use strict";
    var Point = function(x,y) {
         this.x=x;
         this.y=y;
    };
    var Path = function(center,length,both) {
        this.center = center;
        this.length = length;
        this.both   = both;
        this.end    = function(alpha) {
            var retx = 1.0*this.center.x +
                this.length*Math.sin(alpha);
            var rety = 1.0*this.center.y -
                this.length*Math.cos(alpha);
            if (typeof(this.both)!=='undefined')
              return { x1:retx, x2:retx, y1:rety, y2:rety }
            else return { x2:retx, y2:rety };
        };
    };
    var Hand = function(svgline,both) {
        this.elem = document.id(svgline);
        var p     = this.elem.get(['x1','y1','x2','y2']);
        this.path = new Path (
          new Point(p.x1,p.y1),Math.sqrt(
            Math.pow(1.0*p.x2-1.0*p.x1,2) +
            Math.pow(1.0*p.y2-1.0*p.y1,2)) ,both);
        this.setPos = function(angle) {
            this.elem.set(this.path.end(angle));
        };
    };
    var Clock = function(hour,minute,second,refresh) {
        this.hour    = new Hand(hour);
        this.minute  = new Hand(minute);
        this.second  = new Hand(second,true);
        this.refresh = refresh;
        this.setTime = function(timePos) {
            var self= this;
            var tps = 1.0*timePos.getTime() % 60000;
            var tpm = timePos.getMinutes()*1.0 +
                1.0* tps/60000;
            var tph = timePos.getHours()*1.0   + 1.0* tpm/60;
            this.second.setPos(tps / 30000 * Math.PI);
            this.minute.setPos(tpm / 30    * Math.PI);
            this.hour  .setPos(tph / 6     * Math.PI);
            setTimeout(function() {
              self.setTime(new Date())},this.refresh) };
    };
    var clock=new Clock('hours','minutes','seconds',120);
    clock.setTime(new Date());
}());
#box1    { stroke: black; fill:#ccc }
#minutes { stroke: #2288AA; }
#hours   { stroke: #3388CC; }
#seconds { stroke: #CCCC22; }
line,circle {
    opacity:0.65;
    fill:none;
    stroke-width:8;
    stroke-linecap:round;
    stroke-linejoin:round;
    marker:none;
    stroke-miterlimit:4;
    stroke-dasharray:none;
    stroke-opacity:1;
    visibility:visible;
    display:inline;
    overflow:visible;
    enable-background:accumulate
}
<script
  src="http://ajax.googleapis./ajax/libs/mootools/1.4.5/mootools-nopat.js"
  ></script>
<svg xmlns="http://www.w3/2000/svg" id="svg2" width="100%"
     height="100%" viewBox="0 0 900 256" version="1.0">
    <title  id="title1">Clock</title>
    <circle id="box1"    cy="128" cx="128"  r="124" />
    <line   id="hours"   x1="128" y1="128" x2="128"  y2="48" />
    <line   id="minutes" x1="128" y1="128" x2="244" y2="128" />
    <line   id="seconds" x1="128" y1="128" x2="128"   y2="8" />
</svg>

Well, now my new code is 8 lines smaller and even alway readable.

This is a step, but objectization mean make it as an object... With the goals of

  1. Keep it reabable
  2. Keep it efficient (don't make useless operation or the same operation two time)
  3. Make it re-useable (as an object, keep all fixed variables out...)

One intermediary personal result,

But now the code look like:

(function () {
    "use strict";
    var Point = function(x,y) {
         this.x=x;
         this.y=y;
    };
    var Path = function(center,length,both) {
        this.center = center;
        this.length = length;
        this.both   = both;
        this.end    = function(alpha) {
            var retx=1.0*this.center.x+this.length*Math.sin(alpha);
            var rety=1.0*this.center.y-this.length*Math.cos(alpha);
            if (typeof(this.both)!=='undefined')
                 return { x1:retx, x2:retx, y1:rety, y2:rety }
            else return { x2:retx, y2:rety };
        };
    };
    var Hand = function(svgline,both) {
        this.elem   = document.id(svgline);
        var p=this.elem.get(['x1','y1','x2','y2']);
        this.path   = new Path ( new Point(p.x1,p.y1),
                                Math.sqrt(Math.pow(1.0*p.x2-1.0*p.x1,2)+
                                          Math.pow(1.0*p.y2-1.0*p.y1,2)),
                                 both);
        this.setPos = function(angle) {
            this.elem.set(this.path.end(angle));
        };
    };
    var Clock = function(hour,minute,second,refresh) {
        this.hour    = new Hand(hour);
        this.minute  = new Hand(minute);
        this.second  = new Hand(second,true);
        this.setTime = function(timePos) {
            var self= this;
            var tps = 1.0*timePos.getTime() % 60000;
            var tpm = timePos.getMinutes()*1.0 + 1.0* tps/60000;
            var tph = timePos.getHours()*1.0   + 1.0* tpm/60;
            this.second.setPos(tps / 30000 * Math.PI);
            this.minute.setPos(tpm / 30    * Math.PI);
            this.hour  .setPos(tph / 6     * Math.PI);
        };
    };
    var RefreshLoop = function(refresh) {
        var newdate=new Date();
        clock1.setTime(newdate);
        newdate=newdate.getTime()+newdate.getTimezoneOffset()*60000;
        clock2.setTime(new Date(newdate));
        clock3.setTime(new Date(newdate+20700000));
        clock4.setTime(new Date(newdate+28800000));
    };
    var clock1=new Clock('hours','minutes','seconds',120);
    var clock2=new Clock('hours2','minutes2','seconds2',120);
    var clock3=new Clock('hours3','minutes3','seconds3',120);
    var clock4=new Clock('hours4','minutes4','seconds4',120);
    RefreshLoop.periodical(500);
}());

Where all part stay small, my clock is a real reuseable object (now work 4 times).

The function setTime have to pute all hands together as each value hold a portion of other, but each operation have to be done one time only.

For my clock, a Path is defined by a fixed start point a fixed length and a variable direction, the Path.end is the puted end point for a specified direction

And a Hand is a given SVG line, with his original Path, and a optional flag speficying that on setting, both start point and end point have to be positionned at same values (zero length line, with round termination give a round point).

(Remark (bug?): As each elements are defined in the SVG and Path.length is puted from the distance between each end of a Path, the seconds path have to be drawn first with [x1,y1] in the center of the clock, ! = [x2,y2]! )

In the hope some want to correct/improve/discuss my objectization...

UPDATE2

I think, this is now the final version, where object are simple, mootools is used (maybe not too much, remarks weles) and my final clock could be used many time to display different timezome.

(function () {
    "use strict";
    var Point = function(x,y) {
         this.x=x;
         this.y=y;
    };
    var Path = function(center,length,both) {
        this.center = center;
        this.length = length;
        this.both   = both;
        this.end    = function(alpha) {
            var retx=1.0*this.center.x+this.length*Math.sin(alpha);
            var rety=1.0*this.center.y-this.length*Math.cos(alpha);
            if (typeof(this.both)!=='undefined')
                 return { x1:retx, x2:retx, y1:rety, y2:rety }
            else return { x2:retx, y2:rety };
        };
    };
    var Hand = function(svgline,both) {
        this.elem   = document.id(svgline);
        var p=this.elem.get(['x1','y1','x2','y2']);
        this.path   = new Path ( new Point(p.x1,p.y1),
                                Math.sqrt(Math.pow(1.0*p.x2-1.0*p.x1,2)+
                                          Math.pow(1.0*p.y2-1.0*p.y1,2)),
                                 both);
        this.setPos = function(angle) {
            this.elem.set(this.path.end(angle));
        };
    };
    var Clock = function(hour,minute,second,refresh) {
        this.hour    = new Hand(hour);
        this.minute  = new Hand(minute);
        this.second  = new Hand(second,true);
        this.setTime = function(timePos) {
            var self= this;
            var tps = 1.0*timePos.getTime() % 60000;
            var tpm = timePos.getMinutes()*1.0 + 1.0* tps/60000;
            var tph = timePos.getHours()*1.0 + 1.0* tpm/60;
            this.second.setPos(tps / 30000 * Math.PI);
            this.minute.setPos(tpm / 30    * Math.PI);
            this.hour  .setPos(tph / 6     * Math.PI);
        };
    };
    var RefreshLoop = function(refresh) {
        var newdate=new Date();
        clock1.setTime(newdate);
        newdate=newdate.getTime()+newdate.getTimezoneOffset()*60000;
        clock2.setTime(new Date(newdate));
        clock3.setTime(new Date(newdate+20700000));
    };
    var clock1=new Clock('hours','minutes','seconds',120);
    var clock2=new Clock('hours2','minutes2','seconds2',120);
    var clock3=new Clock('hours3','minutes3','seconds3',120);
    RefreshLoop.periodical(500);
}());
circle   { stroke: black; }
.startbg { stop-color: #eeeeee; }
.endbg   { stop-color: #777777; }
.box     { fill:url(#grad0); }
.box1    { fill:url(#grad1); }
.box2    { fill:url(#grad2); }
.box3    { fill:url(#grad3); }
.label   { stroke: #424242;fill:#eee;stroke-width:1; }
.minutes { stroke: #2288AA; }
.hours   { stroke: #3388CC; }
.seconds { stroke: #CCCC22; }
line,circle,rect {
    opacity:0.65;
    fill:none;
    stroke-width:8;
    stroke-linecap:round;
    stroke-linejoin:round;
    marker:none;
    stroke-miterlimit:4;
    stroke-dasharray:none;
    stroke-opacity:1;
    visibility:visible;
    display:inline;
    overflow:visible;
    enable-background:accumulate
}
text {
    font-size:15px;
    font-style:normal;
    font-variant:normal;
    font-weight:normal;
    font-stretch:normal;
    text-align:center;
    line-height:100%;
    writing-mode:lr-tb;
    text-anchor:middle;
    fill:#000000;fill-opacity:.7;
    stroke:none;
    font-family:Nimbus Sans L;
}
<script
  src="http://ajax.googleapis./ajax/libs/mootools/1.4.5/mootools-nopat.js"
  ></script>
<svg xmlns="http://www.w3/2000/svg" id="svg2" width="100%"
    height="100%" viewBox="-1 -1 900 555" version="1.0"><defs>
    <linearGradient gradientUnits="userSpaceOnUse" id="g0"><stop
        class="startbg" /><stop class="endbg" offset="1" /></linearGradient>
    <linearGradient id="grad0" x1="-1"  y1="-1" x2="256" y2="277" xlink:href="#g0" />
    <linearGradient id="grad1" x1="256" y1="-1" x2="515" y2="277" xlink:href="#g0" />
    <linearGradient id="grad2" x1="512" y1="-1" x2="771" y2="277" xlink:href="#g0" />
    </defs>
    <circle class="box"   id="box1" cy="128" cx="128" r="124" />
    <line class="hours"   id="hours" x1="128" y1="128" x2="128" y2="48" />
    <line class="minutes" id="minutes" x1="128" y1="128" x2="244" y2="128" />
    <line class="seconds" id="seconds" x1="128" y1="128" x2="128" y2="8" />
    <rect class="label"   x="16"  y="256" width="224" height="20" />
    <text x="0" y="0" xml:space="preserve">
        <tspan x="128" y="271">Local time</tspan></text>

    <circle class="box1" id="box2" cy="128" cx="385" r="124" />
    <line class="hours" id="hours2" x1="385" y1="128" x2="385" y2="48" />
    <line class="minutes" id="minutes2" x1="385" y1="128" x2="501" y2="128" />
    <line class="seconds" id="seconds2" x1="385" y1="128" x2="385" y2="8" />
    <rect class="label" x="273" y="256" width="224" height="20" />
    <text x="0" y="0" xml:space="preserve">
        <tspan x="385" y="271">Universal Time Clock</tspan></text>

    <circle class="box2" id="box3" cy="128" cx="642" r="124" />
    <line class="hours" id="hours3" x1="642" y1="128" x2="642" y2="48" />
    <line class="minutes" id="minutes3" x1="642" y1="128" x2="758" y2="128" />
    <line class="seconds" id="seconds3" x1="642" y1="128" x2="642" y2="8" />
    <rect class="label" x="530"  y="256" width="224" height="20" />
    <text x="0" y="0" xml:space="preserve">
        <tspan x="642" y="271">Asia/Katmandu</tspan></text>
</svg>

New try pletely rewritten

This use momentjs for intl infos, but no other lib.

"use strict";
var gv={ clockcount: 1,
         svg:'http://www.w3/2000/svg',
         xlnk:'http://www.w3/1999/xlink',
         tzlist:['Local'].concat(moment.tz.names()),
         vbox:document.getElementById('svg').getAttribute("viewBox").split(" ")
       };
function mousepos(event) {
    var minxy=innerWidth;
    if (minxy > innerHeight) minxy=innerHeight;
    return {
        x:((event.clientX-(innerWidth-minxy)/2)/minxy)*(gv.vbox[2]-gv.vbox[0]),
        y:((event.clientY-(innerHeight-minxy)/2)/minxy)*(gv.vbox[3]-gv.vbox[1])
    };
};

function myClock(cx,cy,r,tz) {
    var clock=this, elem;
    this.cx=128;
    if (typeof(cx)!=='undefined') this.cx=cx;
    this.cy=128;
    if (typeof(cy)!=='undefined') this.cy=cy;
    this.r=100;
    if (typeof(r)!=='undefined') this.r=r;
    this.tz=new Date().getTimezoneOffset();
    this.setTz=function(tz) {
        if (typeof(tz)!=='undefined') {
            this.label=tz;
            if (tz!=="Local") {
                var ndte=new Date();
                var tzoff=moment(ndte).tz(tz).format('HH mm ss').split(' ');
                var tznow=Math.floor(ndte/1000)%86400;
                this.tz=(tznow-(tzoff[0]*3600+tzoff[1]*60+1*tzoff[2]))/60;
            } else this.tz=new Date().getTimezoneOffset();
        } else this.label="Local";
    };
    this.setTz(tz);
    this.clkid=gv.clockcount++;
    this.floor=0;
    this.toggleFloor=function(e) { e.preventDefault();
                                   clock.floor=1-clock.floor; };
    this.toggleSecDraw=function(e) { e.preventDefault();
                                     clock.secdraw=1-clock.secdraw; };
    this.wheel=function(e) { e.preventDefault();
                             var sens=1;
                             if (typeof(e.detail)!=='undefined') {
                                 if ( 0 > e.detail ) { sens=-1; }
                             } else if ( 0 > e.wheelDelta ) { sens=-1; };
                             var cidx=gv.tzlist.indexOf(clock.label)*1+1*sens;
                             if (cidx < 0) cidx=gv.tzlist.length-1;
                             if (cidx >= gv.tzlist.length) cidx=0;
                             clock.setTz(gv.tzlist[cidx]);
                             clock.draw=0; };
    this.moused = function (evt) {
        evt.preventDefault(); var m=mousepos(evt);
        if ((clock.r/2 > Math.pow(Math.pow(Math.abs(clock.cx-m.x),2)+
                                  Math.pow(Math.abs(clock.cy-m.y),2),.5))) {
            clock.box.addEventListener("mousemove", clock.mousem, true);
        } else {
            clock.box.addEventListener("mousemove", clock.mouser, true);
        };
        clock.box.addEventListener("mouseup", clock.mouseu, true);
    };
    this.mouseu = function(evt) {
        evt.preventDefault(); clock.draw=0;
        clock.box.removeEventListener("mousemove", clock.mouser, true);
        clock.box.removeEventListener("mousemove", clock.mousem, true);
        clock.box.removeEventListener("mouseup", clock.mouseu, true);
    };
    this.mouser = function(evt) {
        evt.preventDefault(); clock.draw=0;
        var m=mousepos(evt);
        clock.r=1.25*Math.pow(Math.pow(Math.abs(clock.cx-m.x),2)+
                              Math.pow(Math.abs(clock.cy-m.y),2),.5);
    };
    this.mousem = function(evt) { evt.preventDefault(); clock.draw=0;
        var m=mousepos(evt); clock.cx=m.x; clock.cy=m.y; };
    this.drop = function(evt) { evt.preventDefault();clearInterval(clock.loop);
                                clock.box.remove(); };
    elem=document.createElementNS(gv.svg,'g');             
    elem.setAttribute('id','box'+this.clkid);
    document.getElementById('myClock').appendChild(elem);
    this.box=document.getElementById('box'+this.clkid);
    this.box.addEventListener("mousedown",     this.moused ,true);
    this.box.addEventListener("click",         this.toggleSecDraw,true);
    this.box.addEventListener("dblclick",      this.toggleFloor ,true);
    this.box.addEventListener('mousewheel',    this.wheel, true);
    this.box.addEventListener('DOMMouseScroll',this.wheel, true);
    this.box.addEventListener('contextmenu',   this.drop, true);
    
    elem=document.createElementNS(gv.svg,'circle');
    this.fill='fill: url(#g'+this.clkid+');'+
        'stroke: url(#gb'+this.clkid+');';
    elem.setAttribute('style',this.fill);
    elem.setAttribute('id','crc'+this.clkid);
    this.box.appendChild(elem);
    this.crc=document.getElementById('crc'+this.clkid);
    
    this.ticks=[];
    for (var i=0;i<60;i++) {
        elem=document.createElementNS(gv.svg,'line');
        elem.setAttribute('class','ticks');
        elem.setAttribute('id','t'+i+'c'+this.clkid);
        this.box.appendChild(elem);
        this.ticks.push(document.getElementById('t'+i+'c'+this.clkid));
    };
    
    elem=document.createElementNS(gv.svg,'rect');
    elem.setAttribute('class','label');
    elem.setAttribute('id','r'+this.clkid);
    this.box.appendChild(elem);
    this.rct=document.getElementById('r'+this.clkid);
    
    elem=document.createElementNS(gv.svg,'text');
    elem.setAttribute('id','x'+this.clkid);
    this.box.appendChild(elem);
    this.tbx=document.getElementById('x'+this.clkid);
    
    elem=document.createElementNS(gv.svg,'tspan');
    elem.setAttribute('id','t'+this.clkid);
    this.tbx.appendChild(elem);
    this.txt=document.getElementById('t'+this.clkid);

    elem=document.createElementNS(gv.svg,'line');
    elem.setAttribute('id','hr'+this.clkid);
    elem.setAttribute('class','hours');
    this.box.appendChild(elem);
    this.hhr=document.getElementById('hr'+this.clkid);

    elem=document.createElementNS(gv.svg,'line');
    elem.setAttribute('id','mn'+this.clkid);
    elem.setAttribute('class','minutes');
    this.box.appendChild(elem);
    this.hmn=document.getElementById('mn'+this.clkid);

    elem=document.createElementNS(gv.svg,'line'); 
    elem.setAttribute('id','sc'+this.clkid);
    elem.setAttribute('class','seconds');
    this.box.appendChild(elem);
    this.hsc=document.getElementById('sc'+this.clkid);
    
    elem=document.createElementNS(gv.svg,'linearGradient');
    elem.setAttribute('id','g'+this.clkid);
    elem.setAttributeNS(gv.xlnk,'xlink:href','#g0');
    document.getElementById('defs').appendChild(elem);
    this.deg=document.getElementById('g'+this.clkid);
    elem=document.createElementNS(gv.svg,'linearGradient');
    elem.setAttribute('id','gb'+this.clkid);
    elem.setAttributeNS(gv.xlnk,'xlink:href','#g0');
    document.getElementById('defs').appendChild(elem);
    this.dgb=document.getElementById('gb'+this.clkid);

    this.getTZ=function() { return this.tz; };
    this.setTZ=function(tz) { this.tz=tz; };
    this.draw=0;
    this.secdraw=1;
    this.adjust=function() {
        if (clock.draw!==1) {
            clock.crc.setAttribute('style','stroke-width:'+.03*clock.r+";"+
                                  clock.fill);
            clock.hhr.setAttribute('style','stroke-width:'+.11*clock.r);
            clock.hmn.setAttribute('style','stroke-width:'+.075*clock.r);
            clock.hsc.setAttribute('style','stroke-width:'+
                                  (clock.secdraw==1?.03:.09)*clock.r);
            clock.crc.setAttribute('cx',clock.cx);
            clock.crc.setAttribute('cy',clock.cy);
            clock.crc.setAttribute('r',clock.r);
            clock.rct.setAttribute('height',.2*clock.r);
            clock.rct.setAttribute('x',clock.cx-.9*clock.r);
            clock.rct.setAttribute('y',clock.cy*1+1.1*clock.r);
            clock.txt.innerHTML=clock.label;
            clock.txt.setAttribute('x',clock.cx);
            clock.txt.setAttribute('y',clock.cy*1+1.25*clock.r);
            clock.txt.setAttribute('style','font-size: '+(.15*clock.r)+"px;");
            var w=clock.label.length*.1*clock.r+20.0;
            clock.rct.setAttribute('x',clock.cx-w/2);
            clock.rct.setAttribute('width',w);
            for (var i=0;i<60;i++) {
                var x=clock.cx*1+.925*clock.r*Math.sin(i/30*Math.PI);
                var y=clock.cy*1+.925*clock.r*Math.cos(i/30*Math.PI);
                clock.ticks[i].setAttribute('x1',x);
                clock.ticks[i].setAttribute('y1',y);
                clock.ticks[i].setAttribute('x2',x);
                clock.ticks[i].setAttribute('y2',y);
                clock.ticks[i].setAttribute('style','stroke-width:'+
                                            (i%5==0?.04:.02)*clock.r);
            };
            clock.hsc.setAttribute('x1',clock.cx);
            clock.hsc.setAttribute('y1',clock.cy);
            clock.hmn.setAttribute('x1',clock.cx);
            clock.hmn.setAttribute('y1',clock.cy);
            clock.hhr.setAttribute('x1',clock.cx);
            clock.hhr.setAttribute('y1',clock.cy);
            clock.deg.setAttribute('x1',clock.cx-1.1*clock.r);
            clock.deg.setAttribute('y1',clock.cy-1.1*clock.r);
            clock.deg.setAttribute('x2',clock.cx+1.1*clock.r);
            clock.deg.setAttribute('y2',clock.cy+1.1*clock.r);
            clock.dgb.setAttribute('x1',clock.cx+1.1*clock.r);
            clock.dgb.setAttribute('y1',clock.cy+1.1*clock.r);
            clock.dgb.setAttribute('x2',clock.cx-1.1*clock.r);
            clock.dgb.setAttribute('y2',clock.cy-1.1*clock.r);
            clock.draw=1;
        };
        var now=new Date()/1000.0-this.tz*60;
        if (this.floor==1) now=Math.floor(now);
        var x=this.cx+(this.secdraw==1?.975:.925)*
            this.r*Math.sin((now % 60)/30*Math.PI);
        var y=this.cy-(this.secdraw==1?.975:.925)*
            this.r*Math.cos((now % 60)/30*Math.PI);
        this.hsc.setAttribute('x2',x);
        this.hsc.setAttribute('y2',y);
        if (this.secdraw==0) {
            this.hsc.setAttribute('x1',x);
            this.hsc.setAttribute('y1',y);
        }
        if (this.floor==1) now=Math.floor(now/60)         
        else now=now/60;
        x=this.cx+.9*this.r*Math.sin((now %60)/30*Math.PI);
        y=this.cy-.9*this.r*Math.cos((now %60)/30*Math.PI);
        this.hmn.setAttribute('x2',x);
        this.hmn.setAttribute('y2',y);
        if (this.floor==1) now=Math.floor(now/60)         
        else now=now/60;
        x=this.cx+.7*this.r*Math.sin((now % 12)/6*Math.PI);
        y=this.cy-.7*this.r*Math.cos((now % 12)/6*Math.PI);
        this.hhr.setAttribute('x2',x);
        this.hhr.setAttribute('y2',y);
    };
    this.animate = function() {        clock.adjust(); };
    this.loop=setInterval(this.animate,66);
    
};

document.getElementById('svg').addEventListener('dblclick', function(e){ if (e.
 target.id!=='svg')return;var m=mousepos(e);new myClock(m.x,m.y,80,'Local'); });

var clocks=['UTC','Local','Asia/Kolkata'];
for (var i=0;i<3;i++) { new myClock( 90+170*i,90,80,clocks[i]); };
circle   { stroke: black; }
.startbg { stop-color: #CCC; }
.endbg   { stop-color: #222; }
.label   { stroke: #424242;fill:#eee;stroke-width:1; }
.minutes { stroke: #2288AA; }
.hours   { stroke: #3388CC; }
.seconds { stroke: #CCCC22; }
.ticks   { stroke: black; }
line,circle,rect,point {
    opacity:0.65;
    stroke-linecap:round;
    stroke-linejoin:round;
    marker:none;
    stroke-miterlimit:4;
    stroke-dasharray:none;
    stroke-opacity:1;
    visibility:visible;
    display:inline;
    overflow:visible;
    enable-background:accumulate
}
text {
    font-style:normal;
    font-variant:normal;
    font-weight:normal;
    font-stretch:normal;
    text-align:center;
    line-height:100%;
    writing-mode:lr-tb;
    text-anchor:middle;
    fill:#000000;fill-opacity:.7;
    stroke:none;
    font-family:Nimbus Sans L;
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/css" href="myClock2.css" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3/2000/svg" viewBox="0 0 600 600" id="svg"
    xmlns:xlink="http://www.w3/1999/xlink" width="100%" height="100%" >
   <defs id="defs">
     <linearGradient gradientUnits="userSpaceOnUse" id="g0"><stop
       class="startbg" /><stop class="endbg" offset="1" />
     </linearGradient></defs>
 <script type="text/ecmascript"
         xlink:href="http://momentjs./downloads/moment-with-locales.js" />
 <script type="text/ecmascript"
         xlink:href="http://momentjs./downloads/moment-timezone-with-data.js" />
 <title id="title">Clock object</title>
 <g id="myClock"></g>
 <script type="text/ecmascript" xlink:href="myClock2.js" />
 <script type="text/ecmascript">
</script>
</svg>

There is 3 object clock on a svg drawing, with some features:

Mouse mands:

  • when mouse over a clock

    • Click => toggle seconds: path or dot
    • Drag center => move clock
    • Drag border => resize clock
    • Double-click => toggle floor mode
    • Roll mouse wheel => change Timezone
    • Right click (contextmenu) => delete clock
  • when mouse over the background:

    • Double-click => Add a new clock

This is not perfect, as there are some bugs, mostly in mouse positionning with width="100%" height="100%", I think this is an effect of enclosed snippets, but that' not matter here.

You could find a full useable SVG picture on my site

UPDATE:

You could use jQuery or MooTools, but I don't see the point of doing so. jQuery and MooTools are libraries that make DOM Manipulation or AJAX much simpler and the primary purpose of what you are doing with SVG (in this case) does not do much DOM Manipulation and there's no AJAX involved.

That said, if you have your heart set on using one of them (and I'll use jQuery in this answer as I'm not well-versed with MooTools yet), I'd start by wrapping the function call in a $(document).ready() function instead of an IIFE.

$(document).ready(function {
    'use strict';
    ...
});

instead of:

(function () {
    'use strict';
    ...
}());

You could also store the element internally as a jQuery object:

this.element = $('#' + hand);

and then get/set the attributes with slightly less typing:

this.setPosition = function setPosition(now) {
    var x2 = this.element.attr('x2'),
        y2 = this.element.attr('y2');
    this.setInterval(now);
    x2 = this.center.x + (this.length * Math.sin(this.interval / this.frequency * Math.PI));
    y2 = this.center.y - (this.length * Math.cos(this.interval / this.frequency * Math.PI));
    this.element.attr('x2', x2);
    this.element.attr('y2', y2);
    if (this.isSeconds) {
        //special case
        this.element.attr('x1', x2);
        this.element.attr('y1', y2);
    }
};

Not much difference overall, and not worth either the extra overhead or the extra bandwidth (and certainly not worth both) for loading the library in this case.

If you had a more plex example, then it might be worth including either jQuery or MooTools to help out, but in this case I don't think either would add sufficient value to be worth it.

ORIGINAL:

I'm assuming by "objectize this code", you mean to make it more abstract. As it stands, the code you have:

  • does the job
  • is fairly pact
  • is reasonably readable

and is therefore unlikely to be made any smaller (or necessarily "better").

That said, you could abstract a "Hand" object like this:

(function () {
    'use strict';
    var Point = function Point(x, y) {
        this.x = x;
        this.y = y;
    };
    var Hand = function Hand(hand, center, length) {
        this.center = center;
        this.element = document.getElementById(hand);
        this.frequency = 0;
        this.isSeconds = (hand === 'seconds');
        this.hand = hand;
        this.length = length;
        this.interval = 0;
        this.parseMilliseconds = function parseMilliseconds(now) {
            return now.getTime() % 60000;
        };
        this.parseMinutes = function parseMinutes(now) {
            return now.getMinutes() + (this.parseMilliseconds(now) / 60000);
        };
        this.parseHours = function parseHours(now) {
            return now.getHours() + (this.parseMinutes(now) / 60);
        };
        this.setFrequency = function getFrequency() {
            switch (this.hand) {
            case 'hours':
                this.frequency = 6;
                break;
            case 'minutes':
                this.frequency = 30;
                break;
            case 'seconds':
                this.frequency = 30000;
                break;
            }
        };
        this.setInterval = function setInterval(now) {
            switch (this.hand) {
            case 'hours':
                this.interval = this.parseHours(now);
                break;
            case 'minutes':
                this.interval = this.parseMinutes(now);
                break;
            case 'seconds':
                this.interval = this.parseMilliseconds(now);
                break;
            }
        };
        this.setPosition = function setPosition(now) {
            var x2 = this.element.getAttribute('x2'),
                y2 = this.element.getAttribute('y2');
            this.setInterval(now);
            x2 = this.center.x + (this.length * Math.sin(this.interval / this.frequency * Math.PI));
            y2 = this.center.y - (this.length * Math.cos(this.interval / this.frequency * Math.PI));
            this.element.setAttribute('x2', x2);
            this.element.setAttribute('y2', y2);
            if (this.isSeconds) {
                //special case
                this.element.setAttribute('x1', x2);
                this.element.setAttribute('y1', y2);
            }
        };
        this.setFrequency();
    };
    var updateClock = function updateClock(hours, minutes, seconds) {
        var now = new Date();
        hours.setPosition(now);
        minutes.setPosition(now);
        seconds.setPosition(now);
        window.setTimeout(function () {
            updateClock(hours, minutes, seconds);
        }, 80);
    };
    var initClock = function initClock() {
        var center = new Point(128, 128),
            seconds = new Hand('seconds', center, 120),
            minutes = new Hand('minutes', center, 116),
            hours = new Hand('hours', center, 80);
        updateClock(hours, minutes, seconds);
    };
    initClock();
}());

In action here: http://jsfiddle/MbktF/19/

发布评论

评论列表(0)

  1. 暂无评论