javascript - 德国风格的铁路时钟。 SVG 的可怕使用?

标签 javascript css svg

别笑,我对 SVG 的了解几乎。这是我第一次尝试动画 svg 阴影。我想要的只是动态访问过滤器 feOffset 的 dx 和 dy 属性,以便在时钟指针围绕表盘移动时提供逼真的阴影位置。 我能做到的唯一方法是拆开 svg 并用 JavaScript 重新组装它。它工作正常,在我的机器上以大约 1.5% 到 4% 的 cpu 运行,setTimeout 周期约为 30mls(需要平稳的二手)。我丢弃了 requestanimationframe,因为长时间没有页面焦点,时间会花掉。 就目前而言,脚本在每个新周期中创建/替换(我认为!)新 svg。 无论如何,我的问题是有更好/正确的方法来访问和操作 dx 和 dy 吗? Ps:我只使用 svg,因为我想要的形状显然不能用 css 生成,而且时钟是完全可调整大小的,图像 png 等是 Not Acceptable 。 感谢您的帮助。

(function () {

    /* German Station Style Clock */


    /* ^^^^^^^^^^^^ Config below ^^^^^^^^^^^^ */

    var clockSize = 500;
    var casecol = 'rgba(40,40,40,1.0)';
    var dialcol = 'rgba(255,255,255,1.0)';
    var numcol = 'rgba(40,40,40,1.0)';
    var seccol = 'rgba(200,0,0,1.0)';
    var handcol = 'rgba(40,40,40,1.0)';
    var shadowOpacity = 0.3;
    var shadowBlur = 0.2;
    var shadowAngle = 0.6;
    var clockShape = 50;
        /* (max) 50 = round  (min) 0 = square */
    var numoutline = 'no';
        /* 'yes' or 'no' */
    var numfill = 'rgba(255,0,0,1.0)';
    var numshad = 'rgba(0,0,0,0.1)';

    /* ^^^^^^^^^^^^^ End config ^^^^^^^^^^^^^ */

    var d = document;
    var dgts = [];
    var e = 360/12;
    var degr = 0;
    var nums = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
    var tmr;
    var mls = 1000 / 30;
    var radi = Math.PI / 180;
    var offs = 60 * radi;
    var canstroke = ('webkitTextStroke' in d.body.style);
    var str = '-webkit-text-fill-color: '+numfill+';'
        +'-webkit-text-stroke-width: '+xy(0.4)+'px;'
        +'-webkit-text-stroke-color: '+numcol+';';
    var wks = (canstroke && numoutline == "yes")?str:'';
    var broff = (clockShape < 20)?2:0;
    var presec;
    var premin;
    var prehou;
    var rnd = 'id'+Math.random() * 1;
    var idx = d.getElementsByTagName('div').length;
    d.write('<div id = "'+rnd+'" style="display:inline-block;line-height:0px;"></div>');

    function xy (a) {
        return (a * clockSize / 100);
    }

    /* Clock dial */
    var dial = d.createElement('div');
    dial.setAttribute('style', 'display:inline-block;'
        +'position: relative;'
        +'height: '+clockSize+'px;'
        +'width: '+clockSize+'px;'
        +'background-color: '+dialcol+';'
        +'border: '+xy(2)+'px solid '+casecol+';'
        +'border-radius: '+clockShape+'%;');
    d.getElementById(rnd).appendChild(dial);

    /* Clock markers */
    for (var i = 0; i < 12; i++) {
        dgts[i] = d.createElement('div');
        dgts[i].setAttribute('style', 'display: block;'
            +'position: absolute;'
            +'width: '+xy(16)+'px;'
            +'height: '+xy(14)+'px;'
            +'margin: auto;top: 0;bottom: 0; left: 0;right: 0;'
            +'font: bold '+xy(13)+'px  Arial;'
            +'line-height: '+xy(13)+'px;'
            +'text-align: center !important;'
            +'color: '+numcol+';'+wks+';');
        dgts[i].innerHTML = nums[i];
        dial.appendChild(dgts[i]);
        degr += 30;
        dgts[i].style.top = xy(0) + xy(84) * Math.sin(-offs + e * i * radi) + 'px';
        dgts[i].style.left= xy(0) + xy(84) * Math.cos(-offs + e * i * radi) + 'px';
        dgts[i].style.transform = 'rotate(' + (degr) + 'deg)';
        dgts[i].style.transformOrigin = 'center center';
    }

    /* Generic container div for all hands */
    var handContainers = 'display: block;'
        +'position: absolute;'
        +'height: '+xy(100)+'px;'
        +'width: '+xy(20)+'px;'
        +'font-size: 0px; line-height: 0px; padding: 0;'
        +'margin: auto; top: 0;bottom: 0; left: 0; right: 0;'
        +'transform-origin: center center;'

    /* Hour hand */
    var houHand = d.createElement('div');
    houHand.setAttribute('style', handContainers + 'transition: .5s cubic-bezier(0.666, 1.91, 0.333, 0);');
    dial.appendChild(houHand);
    var houC = d.createElement('div');
    var housvg = '<polygon points="94,46 100,40 106,46 106,118 94,118" style="fill:'+handcol+'; stroke:none"/>';

    /* Minute hand */
    var minHand = d.createElement('div');
    minHand.setAttribute('style',handContainers + 'transition: .4s cubic-bezier(0.666, 1.91, 0.333, 0);');
    dial.appendChild(minHand);
    var minC = d.createElement('div');
    var minsvg = '<polygon points="95.5,11.5 100,7 104.5,11.5 104.5,122 95.5,122" style="fill:'+handcol+'; stroke:none"/>';

    /* Seconds hand */
    var secHand = d.createElement('div');
    secHand.setAttribute('style',handContainers);
    dial.appendChild(secHand);
    var secC = d.createElement('div');
    var secsvg = '<polygon points="98.8,11 100,9.8 101.2,11 101.6,42 98.4,42" style="fill:'+seccol+'; stroke:none"/>'+
    '<polygon points="98.1,58 101.9,58 102.5,122 97.5,122" style="fill:'+seccol+'; stroke:none"/>'+
    '<circle cx="100" cy="50" r="8.5" style="fill:none; stroke:'+seccol+'; stroke-width:6.5"/>';

    function dropShadow(s, h) {
        var depth = xy(h);
        var angle = s * radi - shadowAngle;
        var vsa = depth * Math.cos(angle);
        var hsa = depth * Math.sin(angle);
        return {vsa:vsa, hsa:hsa}
    }

    var str1 = '<svg height="'+xy(100)+'" width="'+xy(20)+'" viewBox="90.25 -4 20 200" ><defs>';
    var str3 = '<feGaussianBlur in="SourceAlpha" stdDeviation="'+shadowBlur+'"/>';
    var str5 = '<feFlood flood-color="#000000" flood-opacity="'+shadowOpacity+'"/>'+
    '<feComposite in2="offsetblur" operator="in"/>'+
    '<feMerge>'+
    '<feMergeNode/>'+
    '<feMergeNode in="SourceGraphic"/>'+
    '</feMerge>'+
    '</filter>'+
    '</defs>';
    var str8 = '</g></svg>'; 

    function dynShad (str2, str4, str6, str7) {   
        var create = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8;
        return create;
    }

    function clock() {
        var x = new Date();

        var time    = Math.min(60000, 1.025 * (1000 * x.getSeconds() + x.getMilliseconds()));
        var seconds = Math.floor(time / 1000);
        var millis  = time % 1000;
        var germanSec = (6 * seconds + 3 * (1 + Math.cos(Math.PI + Math.PI * (0.001 * millis))));
        var minutes = x.getMinutes();
        var hours = (x.getHours() * 30) + (minutes / 2);



        if (germanSec !== presec) {
            var ssy = dropShadow(germanSec, 0.7).vsa;
            var ssx = dropShadow(germanSec, 0.7).hsa;
            var sf = '<filter id="sf'+idx+'" x="-50%" y="-50%" width="200%" height="200%">';
            var se = '<g filter="url(#sf'+idx+')">';
            var ss = '<feOffset id="soffset'+idx+'" dx="'+ssx+'" dy="'+ssy+'" result="offsetblur"/>';
            secC.innerHTML = dynShad (sf, ss,se, secsvg);
            secHand.appendChild(secC);
        }
        
        if (minutes !== premin) {
            var msy = dropShadow(minutes * 6, 0.5).vsa;
            var msx = dropShadow(minutes * 6, 0.5).hsa;
            var mf = '<filter id="mf'+idx+'" x="-50%" y="-50%" width="200%" height="200%">';
            var me ='<g filter="url(#mf'+idx+')">';
            var ms = '<feOffset id="moffset'+idx+'"  dx="'+msx+'" dy="'+msy+'" result="offsetblur"/>';
            minC.innerHTML = dynShad (mf, ms,me, minsvg);
            minHand.appendChild(minC);
        }
        
        if (hours !== prehou) {
            var hsy = dropShadow(hours, 0.4).vsa;
            var hsx = dropShadow(hours, 0.4).hsa;
            var hf = '<filter id="hf'+idx+'" x="-50%" y="-50%" width="200%" height="200%">';
            var he ='<g filter="url(#hf'+idx+')">';
            var hs = '<feOffset id="hoffset'+idx+'" dx="'+hsx+'" dy="'+hsy+'" result="offsetblur"/>';
            houC.innerHTML = dynShad (hf, hs,he, housvg);
            houHand.appendChild(houC);
        }
        
        secHand.style.transform = 'rotate(' + germanSec + 'deg) translateZ(0)'; 
        minHand.style.transform = 'rotate(' + (minutes * 6) + 'deg) translateZ(0)';
        houHand.style.transform = 'rotate(' + hours + 'deg) translateZ(0)';

        presec = germanSec;
        premin = minutes;
        prehou = hours;

        tmr = setTimeout(clock, mls);
    }

    window.addEventListener('load', clock, false);
})();

最佳答案

All I wanted was to dynamically access the filter feOffset's dx and dy attributes to give the clock hands realistic shadow positions as they move around the dial.

实际上,您不必为了逼真的阴影而触及 dx 和 dy 属性。您所要做的就是将手放在 g 元素上并将阴影应用于组(不旋转)。在这种情况下,阴影不会旋转,因此偏移量保持不变。

看这个例子,矩形在旋转,但是阴影被应用到组中:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200" height="200">
	<defs>
		<filter id="gb" filterUnits="userSpaceOnUse" x="-50" y="-50" width="100" height="100">
		    <feGaussianBlur in="SourceAlpha" stdDeviation="1" />
		    <feOffset dx="2" dy="2" />
		    <feMerge>
		        <feMergeNode />
		        <feMergeNode in="SourceGraphic" />
		    </feMerge>
	  	</filter>
	</defs>
	<g transform="translate(50 50)" filter="url(#gb)">
		<rect  x="-0.5" y="-45" width="1" height="45" fill="red">
			<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="10s" repeatCount="indefinite"/>
		</rect>
	</g>
</svg>

var r=document.getElementById("rect")

setInterval(function(){
var d = new Date();
var a = d.getSeconds()*6; // 360/60 so every second equals 6 deg
r.setAttribute("transform","rotate("+a+")");
},500);
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200" height="200">
	<defs>
		<filter id="gb" filterUnits="userSpaceOnUse" x="-50" y="-50" width="100" height="100">
		    <feGaussianBlur in="SourceAlpha" stdDeviation="1" />
		    <feOffset dx="2" dy="2" />
		    <feMerge>
		        <feMergeNode />
		        <feMergeNode in="SourceGraphic" />
		    </feMerge>
	  	</filter>
	</defs>
	<g transform="translate(50 50)" filter="url(#gb)">
		<rect id="rect" x="-0.5" y="-45" width="1" height="45" fill="red"/>
	</g>
</svg>

关于javascript - 德国风格的铁路时钟。 SVG 的可怕使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32810205/

相关文章:

reactjs - 在 create-react-app 中高效导入多个相似的 svg 源?

javascript - D3.js 移动 svg 结构中的元素

javascript - 我们是否需要手动清理闭包中未引用的变量?

html - 浏览器缓存干扰 css 样式更改?

ios - 如何获取 SVGElement 所属的组?

html - 为什么页面在 IE 中的显示与谷歌浏览器不同?

iphone - 如何解决在 iPhone 版 Refinery CMS 中不起作用的媒体查询?

javascript - 迭代复杂对象,删除不需要的元素(多个嵌套对象)

javascript - jQuery $.each() 方法未按预期运行

javascript - 自动调整 span 的字体以适应 div