43337


下記のコードは、アナログ時計の外周を、1分ごとに"和暦"と"西暦"に切り変わるものですが、  
問題箇所が2点あるのです。

1点目は、暦は"toLocaleDateString"を使っていますが、西暦に比べて和暦の方が重い(遅い)  
のです。原因は、何でしょうか。  
移動速度(step)は、0.06に設定してあります。

2点目は、仮に現在が"平成99年(9999年)だとすると、来年は"平成100年(10000年)"となり、  
文字数が1つ増えます。このように"和暦"や"西暦"の年の桁が増加するごとに、1文字抜け落ちて  
表示されます。  
この問題を解決するには、コード内の『var TodaysDate = " ";』の" "内のスペースを  
増やすだけでいいのですが、何か式を使ってできないでしょうか。

[code]

    <html>
    <head>
        <meta charset="utf-8" />
        <title>１分おき日英切り替え</title>
    </head>
    <body>
    <div id="clock">
        <div id="Od" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Of" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Oh" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Om" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Os" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
    </div>
    
    <script type="text/javascript">
    
    (function(){
        "use strict";
    
    function $(sel)
    {
        return document.getElementById(sel);
    }
    
    function $$(sel)
    {
        if (typeof document.getElementsByClassName === 'undefined')
        {
            return document.getElementsByName(sel);
        }
    
        return document.getElementsByClassName(sel);
    }
    
    
    var dCol = '00ff00', //date colour.
        sCol = 'ff0000', //seconds colour.
        mCol = '000000', //minutes colour.
        hCol = '000000', //hours colour.
        fCol = '0000ff', //face color
        ClockHeight = 40,
        ClockWidth = 40,
        ClockFromMouseY = 0,
        ClockFromMouseX = 100;
    
    var TodaysDate = "     ";
    
      /*最大文字数を5文字に設定(""内に半角で、5文字分スペースを作る)。*/
    
    
    var D = TodaysDate.split('');
    var H = '...';
        H = H.split('');
    var M = '....';
        M = M.split('');
    var S = '.....';
        S = S.split('');
    var Face = '1 2 3 4 5 6 7 8 9 10 11 12',
        font = 'Helvetica, Arial, sans-serif',
        size = 1,
        speed = 0.6;
        Face = Face.split(' ');
    var n = Face.length;
    var a = size * 10;
    var ymouse = 0,
        xmouse = 0,
        scrll = 0,
        props = '<span style="font-family:' + font + ';font-size:' + size + 'em; color:#' + fCol + '">';
    var Split = 360 / n;
    var HandHeight = ClockHeight / 4.5; 
    var HandWidth = ClockWidth / 4.5;
    var HandY = -7,
        HandX = -2.5,
        step = 0.06,
        currStep = 0,
        y = [],
        x = [],
        Y = [],
        X = [],
        Dy = [],
        Dx = [],
        DY = [],
        DX = [];
    
    var i;
    
    for (i = 0; i < n; i++) 
    {
        y[i] = 0;
        x[i] = 0;
        Y[i] = 0;
        X[i] = 0;
    }
    
    for (i = 0; i < D.length; i++) 
    {
        Dy[i] = 0;
        Dx[i] = 0;
        DY[i] = 0;
        DX[i] = 0;
    }
    
    var wrapper = $('clock');
    var html = '';
    
    
    
    // Face wrapper
    html = '';
    for (i = 0; i < n; i++) 
    {
        html += '<div class="Face" name="Face" style="position:absolute;top:0px;left:0;height:' + a + ';width:' + a + ';text-align:center">' + props + Face[i] + '</span></div>';
    }
    $('Of').children[0].innerHTML = html;
    
    // Hours wrapper
    html = '';
    for (i = 0; i < H.length; i++)
    {
        html += '<div class="Hours" name="Hours" style="position:absolute;width:16px;height:16px;font-family:Arial;font-size:16px;color:' + hCol + ';text-align:center;font-weight:bold">' + H[i] + '</div>';
    } 
    $('Oh').children[0].innerHTML = html;
    
    // Minute wrapper
    html = '';
    for (i = 0; i < M.length; i++)
    {
        html += '<div class="Minutes" name="Minutes" style="position:absolute;width:16px;height:16px;font-family:Arial;font-size:16px;color:' + mCol + ';text-align:center;font-weight:bold">' + M[i] + '</div>';
    } 
    $('Om').children[0].innerHTML = html;
    
    // Seconds wrapper
    html = '';
    for (i = 0; i < S.length; i++)
    {
        html += '<div class="Seconds" name="Seconds" style="position:absolute;width:16px;height:16px;font-family:Arial;font-size:16px;color:' + sCol + ';text-align:center;font-weight:bold">' + S[i] + '</div>';
    } 
    $('Os').children[0].innerHTML = html;
    
    // Mouse move event handler
    function Mouse(evnt) 
    {
        if (typeof evnt === 'undefined')
        {
            ymouse = event.Y + ClockFromMouseY;
            xmouse = event.X + ClockFromMouseX;
        }
        else
        {
            ymouse = evnt.clientY + ClockFromMouseY;
            xmouse = evnt.clientX + ClockFromMouseX;
        }
    }
    document.onmousemove = Mouse;
    
    function ClockAndAssign() 
    {
    
    var date = new Date(),
        mt = date.getMinutes();
    
          if( mt%2==0 ){
    
    
    var date = new Date();
    
        TodaysDate = date.toLocaleDateString("ja-JP-u-ca-japanese", { era: "long", year: "numeric" }).replace(/\u200e/g, "").replace(" ", "");
    
     }     else{
    
    
        TodaysDate = date.toLocaleDateString("en-US", { era: "long", year: "numeric" }).replace(/\u200e/g, "") + "年";
    
      }
    
        var D = TodaysDate.split('');
        var props2 = '<span style="font-family:' + font + ';font-size:' + size + 'em; color:#' + dCol + '">';
        var Dsplit = 360 / D.length;
    
    // Date wrapper
    html = '';
    
    for (i = 0; i < D.length; i++){
        html += '<div class="Date" name="Date" style="position:absolute;top:0px;left:0;height:' + a + ';width:' + a + ';text-align:center">' + props2 + D[i] + '</span></div>';
    }
    $('Od').children[0].innerHTML = html;
    
    
        var time = new Date();
        var secs = time.getSeconds();
        var sec = -1.57 + Math.PI * secs / 30;
        var mins = time.getMinutes();
        var min = -1.57 + Math.PI * mins / 30;
        var hr = time.getHours();
        var hrs = -1.575 + Math.PI * hr / 6 + Math.PI * parseInt(time.getMinutes(), 10) / 360;
        $('Od').style.top = window.document.body.scrollTop;
        $('Of').style.top = window.document.body.scrollTop;
        $('Oh').style.top = window.document.body.scrollTop;
        $('Om').style.top = window.document.body.scrollTop;
        $('Os').style.top = window.document.body.scrollTop;
        for (i = 0; i < n; i++)
        {
            var F = $$('Face')[i];
            F.style.top = y[i] + ClockHeight * Math.sin(-1.0471 + i * Split * Math.PI / 180) + scrll;
            F.style.left = x[i] + ClockWidth * Math.cos(-1.0471 + i * Split * Math.PI / 180);
        }
        for (i = 0; i < H.length; i++)
        {
            var HL = $$('Hours')[i];
            HL.style.top = y[i] + HandY + (i * HandHeight) * Math.sin(hrs) + scrll;
            HL.style.left = x[i] + HandX + (i * HandWidth) * Math.cos(hrs);
        }
        for (i = 0; i < M.length; i++)
        {
            var ML = $$('Minutes')[i].style;
            ML.top = y[i] + HandY + (i * HandHeight) * Math.sin(min) + scrll;
            ML.left = x[i] + HandX + (i * HandWidth) * Math.cos(min);
        }
        for (i = 0; i < S.length; i++)
        {
            var SL = $$('Seconds')[i].style;
            SL.top = y[i] + HandY + (i * HandHeight) * Math.sin(sec) + scrll;
            SL.left = x[i] + HandX + (i * HandWidth) * Math.cos(sec);
        }
        for (i = 0; i < D.length; i++)
        {
            var DL = $$('Date')[i].style;
            DL.top = Dy[i] + ClockHeight * 1.5 * Math.sin(currStep + i * Dsplit * Math.PI / 180) + scrll;
            DL.left = Dx[i] + ClockWidth * 1.5 * Math.cos(currStep + i * Dsplit * Math.PI / 180);
        }
        currStep -= step;
    }
    
    function Delay() 
    {
        scrll = 0;
        Dy[0] = Math.round(DY[0] += ((ymouse) - DY[0]) * speed);
        Dx[0] = Math.round(DX[0] += ((xmouse) - DX[0]) * speed);
        for (i = 1; i < D.length; i++) {
            Dy[i] = Math.round(DY[i] += (Dy[i - 1] - DY[i]) * speed);
            Dx[i] = Math.round(DX[i] += (Dx[i - 1] - DX[i]) * speed);
        }
        y[0] = Math.round(Y[0] += ((ymouse) - Y[0]) * speed);
        x[0] = Math.round(X[0] += ((xmouse) - X[0]) * speed);
        for (i = 1; i < n; i++) {
            y[i] = Math.round(Y[i] += (y[i - 1] - Y[i]) * speed);
            x[i] = Math.round(X[i] += (x[i - 1] - X[i]) * speed);
        }
        ClockAndAssign();
        setTimeout(Delay, 20);
    }
    
    Delay();
    }());
    </script>
    </body>
    </html>
    
[/code]


CC BY-SA 3.0
2018-04-17T11:28:37.690
43328
2018-04-18T02:04:43.570
28063
question
javascript
"toLocaleDateString"を使っての"和暦"の移動速度が重い原因について、など。


## 1点目

`date.toLocaleDateString("ja-JP-u-ca-japanese", ...)` が IE11 と Edge
で尋常でなく遅いですね。`toLocalDateString` を1000回呼ぶ時間を計ってみました。

IE11:  
ja-JP-...: 63086 [ms]  
en-US: 697 [ms]

Edge:  
ja-JP-...: 84628 [ms]  
en-US: 577 [ms]

Chrome:  
ja-JP-...: 1092 [ms]  
en-US: 811 [ms]

Firefox:  
ja-JP-...: 908 [ms]  
en-US: 594 [ms]

IE と Edge では ja-JP-... の方が en-US にくらべて **90〜160倍** も時間がかかっています。

分が変わっていなければ前回使った文字列を使い回す、などの処理で`toLocalDateString` を呼ぶ回数を減らすしかないと思います。

## 2点目

class="Date" の要素数を `toLocalDateString` の結果から動的に決めればよいです。`DX`, `DY`, `Dx`, `Dy`
の要素数も動的にする必要があります。問題は文字数が長くなったときに参照すべき座標の値が存在しないことで、こういう場合は最後の要素の座標を採用するなどで調整するといいかもしれません。

ざっと書き換えたコードを掲載します。関数 `updateYear()` のあたりが両方の問題に対処しています。

[code]

    "use strict";
    
    function $(sel) {
      return document.getElementById(sel);
    }
    
    function $$(sel) {
      return document.getElementsByClassName(sel);
    }
    
    function setPosition(element, y, x) {
      element.style.top = y + 'px';
      element.style.left = x + 'px';
    }
    
    const CLOCK_HEIGHT = 40,
        CLOCK_WIDTH = 40,
        CLOCK_FROM_MOUSE_Y = 0,
        CLOCK_FROM_MOUSE_X = 100;
    
    const H = '...'.split('');
    const M = '....'.split('');
    const S = '.....'.split('');
    const SPEED = 0.6,
        FACES = '1 2 3 4 5 6 7 8 9 10 11 12'.split(' ');
    const HAND_HEIGHT = CLOCK_HEIGHT / 4.5; 
    const HAND_WIDTH = CLOCK_WIDTH / 4.5;
    const HAND_Y = -7,
        HAND_X = -2.5,
        STEP = 0.06;
    
    var ymouse = 0,
        xmouse = 0;
    var currStep = 0;
    var lastBasePositions = [];
    
    function initialize() {
      for (var i = 0; i < FACES.length; ++i) {
        lastBasePositions[i] = {x:0, y:0};
      }
    
      var html = '';
      // Face wrapper
      html = '';
      for (var i = 0; i < FACES.length; ++i) {
        html += '<div class="Face">' + FACES[i] + '</div>';
      }
      $('Of').children[0].innerHTML = html;
    
      // Hours wrapper
      html = '';
      for (var i = 0; i < H.length; ++i) {
        html += '<div class="Hours">' + H[i] + '</div>';
      } 
      $('Oh').children[0].innerHTML = html;
    
      // Minute wrapper
      html = '';
      for (var i = 0; i < M.length; ++i) {
        html += '<div class="Minutes">' + M[i] + '</div>';
      }
      $('Om').children[0].innerHTML = html;
    
      // Seconds wrapper
      html = '';
      for (var i = 0; i < S.length; ++i) {
        html += '<div class="Seconds">' + S[i] + '</div>';
      }
      $('Os').children[0].innerHTML = html;
    
      // Mouse move event handler
      document.onmousemove = function(evnt) {
        ymouse = evnt.clientY + CLOCK_FROM_MOUSE_Y;
        xmouse = evnt.clientX + CLOCK_FROM_MOUSE_X;
      };
    
      requestAnimationFrame(ClockAndAssign);
    }
    
    var lastYearPositions = [{x:0, y:0}];
    var lastYearString = ' ';
    var lastYearLocale = '';
    function updateYear(currentDate, scrll) {
      var yearString = lastYearString;
      if (currentDate.getMinutes() % 2 == 0) {
        if (lastYearLocale != 'ja')
          yearString = currentDate.toLocaleDateString("ja-JP-u-ca-japanese", { era: "long", year: "numeric" }).
              replace(/\u200e/g, "").replace(" ", "");
        lastYearLocale = 'ja';
      } else {
        if (lastYearLocale != 'en')
          yearString = currentDate.toLocaleDateString("en-US", { era: "long", year: "numeric" }).
              replace(/\u200e/g, "") + "年";
        lastYearLocale = 'en';
      }
    
      var yearLength = lastYearPositions.length;
      if (yearString != lastYearString) {
        lastYearString = yearString;        
        var yearCharacters = yearString.split('');
        yearLength = yearCharacters.length;
    
        // Date wrapper
        var html = '';
        for (var i = 0; i < yearLength; ++i) {
          html += '<div class="Date">' + yearCharacters[i] + '</div>';
        }
        $('Od').children[0].innerHTML = html;
      }
      var positions = [{}];
      var lastPosition = lastYearPositions[0];
      positions[0].y = lastPosition.y + ((ymouse) - lastPosition.y) * SPEED;
      positions[0].x = lastPosition.x + ((xmouse) - lastPosition.x) * SPEED;
      for (var i = 1; i < yearLength; ++i) {
        lastPosition = i < lastYearPositions.length ?
            lastYearPositions[i] :
            lastYearPositions[lastYearPositions.length - 1];
        positions[i] = {};
        positions[i].y = lastPosition.y + (positions[i-1].y - lastPosition.y) * SPEED;
        positions[i].x = lastPosition.x + (positions[i-1].x - lastPosition.x) * SPEED;
      }
      for (var i = 0; i < yearLength; ++i) {
        var radian = currStep + i * (360 / yearLength) * Math.PI / 180;
        setPosition($$('Date')[i],
                    Math.round(positions[i].y) + CLOCK_HEIGHT * 1.5 * Math.sin(radian) + scrll,
                    Math.round(positions[i].x) + CLOCK_WIDTH * 1.5 * Math.cos(radian));
      }
      lastYearPositions = positions;
      currStep -= STEP;
    }
    
    function ClockAndAssign() {
      var date = new Date();
      var secs = date.getSeconds();
      var sec = -1.57 + Math.PI * secs / 30;
      var mins = date.getMinutes();
      var min = -1.57 + Math.PI * mins / 30;
      var hr = date.getHours();
      var hrs = -1.575 + Math.PI * hr / 6 + Math.PI * parseInt(date.getMinutes(), 10) / 360;
      $('Od').style.top = window.document.body.scrollTop;
      $('Of').style.top = window.document.body.scrollTop;
      $('Oh').style.top = window.document.body.scrollTop;
      $('Om').style.top = window.document.body.scrollTop;
      $('Os').style.top = window.document.body.scrollTop;
      var scrll = 0;
    
      var positions = [{}];
      var lastPosition = lastBasePositions[0];
      positions[0].y = Math.round(lastPosition.y + (ymouse - lastPosition.y) * SPEED);
      positions[0].x = Math.round(lastPosition.x + (xmouse - lastPosition.x) * SPEED);
      for (var i = 1; i < FACES.length; ++i) {
        lastPosition = lastBasePositions[i];
        positions[i] = {};
        positions[i].y = Math.round(lastPosition.y + (positions[i - 1].y - lastPosition.y) * SPEED);
        positions[i].x = Math.round(lastPosition.x + (positions[i - 1].x - lastPosition.x) * SPEED);
      }
      lastBasePositions = positions;
    
      var split = 360 / FACES.length;
      for (var i = 0; i < FACES.length; ++i) {
        var radian = -1.0471 + i * split * Math.PI / 180;
        setPosition($$('Face')[i],
                    positions[i].y + CLOCK_HEIGHT * Math.sin(radian) + scrll,
                    positions[i].x + CLOCK_WIDTH * Math.cos(radian));
      }
      for (var i = 0; i < H.length; ++i) {
        setPosition($$('Hours')[i],
                    positions[i].y + HAND_Y + (i * HAND_HEIGHT) * Math.sin(hrs) + scrll,
                    positions[i].x + HAND_X + (i * HAND_WIDTH) * Math.cos(hrs));
      }
      for (var i = 0; i < M.length; ++i) {
        setPosition($$('Minutes')[i],
                    positions[i].y + HAND_Y + (i * HAND_HEIGHT) * Math.sin(min) + scrll,
                    positions[i].x + HAND_X + (i * HAND_WIDTH) * Math.cos(min));
      }
      for (var i = 0; i < S.length; ++i) {
        setPosition($$('Seconds')[i],
                    positions[i].y + HAND_Y + (i * HAND_HEIGHT) * Math.sin(sec) + scrll,
                    positions[i].x + HAND_X + (i * HAND_WIDTH) * Math.cos(sec));
      }
      updateYear(date, scrll);
      requestAnimationFrame(ClockAndAssign);
    }
    
    initialize();
[/code]

[code]

    body {
      font-family: Helvetica, Arial, sans-serif;
    }
    
    .Face {
      position: absolute;
      height: 10;
      widdth: 10;
      text-align: center;
      font-size: 1em;
      color: #0000ff;
    }
    
    .Hours {
      position: absolute;
      width: 16px;
      height: 16px;
      font-family: Arial;
      font-size: 16px;
      color: #000000;
      text-align: center;
      font-weight: bold;
    }
    
    .Minutes {
      position: absolute;
      width: 16px;
      height: 16px;
      font-family: Arial;
      font-size: 16px;
      color: #000000;
      text-align: center;
      font-weight: bold;
    }
    
    .Seconds {
      position: absolute;
      width: 16px;
      height: 16px;
      font-family: Arial;
      font-size: 16px;
      color: #ff0000;
      text-align: center;
      font-weight: bold;
    }
    
    .Date {
      position: absolute;
      height: 10;
      width: 10;
      font-size: 1em;
      color: #00ff00;
    }
[/code]

[code]

    <div id="clock">
        <div id="Od" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Of" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Oh" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Om" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
        <div id="Os" style="position:absolute;top:0px;left:0px">
            <div style="position:relative">
            </div>
        </div>
    </div>
[/code]


CC BY-SA 3.0
2018-04-18T02:04:43.570
43337
2018-04-18T02:04:43.570
3475
43328
answer