Javascript 实现[网红] 时间轮盘
话不多说,先上图。
成品链接
大致效果如上图,接下来就开始制作吧。
HTML部分:
我们需要将容器旋转rotate使之以圆点为中心。
怎么转呢,请看图。
将同一级的容器用一个大的容器包裹起来,绝对定位。这时,所有的子容器会重合在一起。
然后我们用360°去除以子容器个数,可以得到每个子容器的角度差,采用角度差来旋转每个容器。
效果如图:
我们可以看到,他是重合在一起的,应位默认的旋转中心是容器的中心,
这时我们需要改变旋转中心点,
添加css属性 transform-origin: -50% 50%;
效果如图:
我们可以发现此时旋转过后的图形超出了父容器,所以需要改变他的位置,根据旋转可知。
图形总长度为子容器长度的3倍(中间空白处为一个子容器长度)
高度为 空白处高度加上 子容器高度的两倍
经过位置变换(再填上文字)
效果如图:
以上讲解代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>时间轮盘 一</title> <style> #wrapper { width: 500px; height: 500px; margin: 100px auto; background: #ccc; } .container { width: 100%; height: 100%; position: relative; left: 300px; top: 210px; } .box { width: 150px; height: 30px; background: orange; /* border: 1px solid black; */ position: absolute; top: 0; left: 0; transform-origin: -50% 50%; } </style> </head> <body> <div id="wrapper"> <div class="container"> <div class="box">我是1</div> <div class="box" style="transform: rotate(45deg)">我是2</div> <div class="box" style="transform: rotate(90deg)">我是3</div> <div class="box" style="transform: rotate(135deg)">我是4</div> <div class="box" style="transform: rotate(180deg)">我是5</div> <div class="box" style="transform: rotate(225deg)">我是6</div> <div class="box" style="transform: rotate(270deg)">我是7</div> <div class="box" style="transform: rotate(315deg)">我是</div> </div> </div> </body> </html>
基本布局就是这样,接下来我们需要多制作几个,分别存放星期、时分秒。
成品HTML代码部分如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="css/TimeRoulette.css"> <title>时间轮盘</title> </head> <body> <div class="wrapper"> <h1 class="workTitle">时间轮盘</h1> <p class="one-text">天可补,海可填,南山可移,日月既往,不可复追。</p> <div class="timeBox"> <div class="year-wrapper" title="点击更换背景图片"> <p></p> </div> <div class="week-wrapper"> <ul class="week-content"> </ul> </div> <div class="hours-wrapper"> <ul class="hours-content"> </ul> </div> <div class="minutes-wrapper"> <ul class="minutes-content"> </ul> </div> <div class="seconds-wrapper"> <ul class="seconds-content"> </ul> </div> </div> </div> </body> <script src="js/jquery.js"></script> <script src="js/TimeRoulette.js"></script> </html>
大家可以发现,我们的hrml中并没有数据,有关星期,时分秒,的数据我们通过js生成,毕竟太多了,而且有规律可循。
这里我们使用了jquery来实现dom操作
接下来就是JS部分了!
首先我们需要几个函数,将数字转换成大写(仅做了1000以内),将年份转化为干纪年法(显得霸气),将24小时转化为时辰(实现一半,所以此处未加上)
// 数字大小写转换 function numberToZh(num) { var zh = ["零","壹","贰","叁","肆","伍","陆","柒","捌","玖","拾","佰","仟","万"]; var res = ""; if(num <= 10) { res = zh[num]; } else if(num < 20) { var bits = num % 10; res = zh[10] + zh[bits]; } else if(num < 100) { var bits = num % 10; var decade = parseInt(num / 10); if(bits == 0) { res = zh[decade] + zh[10] + "整"; } else { res = zh[decade] + zh[10] + zh[bits]; } } return res; } // 干支纪年法 function toAncientYear(year) { var sky = ["", "辛", "壬", "癸", "甲", "乙", "丙", "丁", "戊", "己", "庚"]; var land = ["", "酉", "戌", "亥", "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申"]; // 用年份除以10得数中余数相对应的便是天干 var one = year % 10; // 用年份除以12得数中余数相对应的便是地支 var two = year % 12; var res = sky[one] + land[two]; return res; } //时间转时辰 function toAncientHours(num) { var res = ""; switch(num) { case 1 : res = "子";break; case 2 : res = "丑";break; case 3 : res = "寅";break; case 4 : res = "卯";break; case 5 : res = "辰";break; case 6 : res = "巳";break; case 7 : res = "午";break; case 8 : res = "未";break; case 9 : res = "申";break; case 10 : res = "酉";break; case 11 : res = "戌";break; case 12 : res = "亥";break; } return res; }
有了这几个小函数,我们就可以初始化时间轮盘了
代码:
function init() { // 更换背景图片 $(".timeBox .year-wrapper").click(function(){ $("body").css("background-image", "url(images/background"+ Math.ceil(Math.random()*4) +".jpg)") }) // 设置文本值 setNumText(); // 设置干支纪年时间 var nowYear = (new Date).getFullYear(); var acientYear = toAncientYear(nowYear); $(".year-wrapper p").text(acientYear).attr("title","公元" + nowYear + "农历" + acientYear +"年"); // 初始化内容位置,旋转到指定角度 var weekLen = $(".week-content li").length; var weekDeg = 360/weekLen; $(".week-content li").each(function(index) { $(this).css({ "transform":"rotate("+index*weekDeg+"deg)", "transform-origin":"-100% 50%", "margin-left":parseInt($(this).css("width")) * 3 + "px", "margin-top":parseInt($(this).css("width")) * 2 - 10 + "px" }) }) // 时 var hoursLen = $(".hours-content li").length; var hoursDeg = 360/hoursLen; $(".hours-content li").each(function(index) { $(this).css({ "transform":"rotate("+index*hoursDeg+"deg)", "transform-origin":"-250% 50%", "margin-left":parseInt($(this).css("width")) * 6 + "px", "margin-top":parseInt($(this).css("width")) * 3.5 - 10 + "px" }) }) //分 var minutesLen = $(".minutes-content li").length; var minutesDeg = 360/minutesLen; $(".minutes-content li").each(function(index) { $(this).css({ "transform":"rotate("+index*minutesDeg+"deg)", "transform-origin":"-400% 50%", "margin-left":parseInt($(this).css("width")) * 9 + "px", "margin-top":parseInt($(this).css("width")) * 5 - 10 + "px" }) }) //秒 var secondsLen = $(".seconds-content li").length; var secondsDeg = 360/secondsLen; $(".seconds-content li").each(function(index) { $(this).css({ "transform":"rotate("+index*secondsDeg+"deg)", "transform-origin":"-550% 50%", "margin-left":parseInt($(this).css("width")) * 12 + "px", "margin-top":parseInt($(this).css("width")) * 6.5 - 10 + "px" }) }) //每秒刷新一次 run(); }
// 设置文本内容 function setNumText(){ for(var i = 7; i > 0; i --) { $(".week-content").append("<li data-time = "+ i +"> 星期"+ numberToZh(i) +"<li>") } for(var i = 12;i > 0; i --) { $(".hours-content").append("<li data-time = "+ i +">"+ numberToZh(i) +"时<li>") } for(var i = 60;i > 0; i --) { $(".minutes-content").append("<li data-time = "+ i +">"+ numberToZh(i) +"<li>") } for(var i = 60;i > 0; i --) { $(".seconds-content").append("<li data-time = "+ i +">"+ numberToZh(i) +"<li>") } }
此时,项目的静态效果已经有了,我们需要让他动起来,也就是上面代码中的run()函数
// 刷新轮盘 function run() { clearInterval(timer); var date = new Date();//获取本地时间 // 分别获取时分秒年 var week = date.getDay(); var hours = date.getHours() % 12; var minutes = date.getMinutes(); var seconds = date.getSeconds(); // 计算对应的旋转角度差 // rotateIndexH 为定义的全局变量,存储了小时的旋转圈数 // rotateIndexM、rotateIndexS 同理为分、秒的 // week var weekRote = 360 / 7 * (week); // hours var hoursRote = 360 / 12 * (hours) + rotateIndexH * 360; // minites secondes var tempDeg = 360 / 60; var minutesRote = tempDeg * (minutes) + rotateIndexM * 360; var secondsRote = tempDeg * (seconds) + rotateIndexS * 360; //var secondsRote = tempDeg * (seconds + 1) + rotateIndexS*360; // 加一是为了滚到位置再变色,去掉会先变色再滚到位置 // 旋转 秒 的位置 $(".seconds-wrapper").css("transform", "rotate(" + secondsRote + "deg)"); // 点亮 由于时间是从0开始计数的所以这里判断 var secondDot = seconds == 0 ? 60 : seconds; var minuteDot = minutes == 0 ? 60 : minutes; var hourDot = hours == 0 ? 60 : hours; var weekDot = hours == 0 ? 7 : week; // $(".hour-content li[data-time="+hourDot+"]").addClass("active").next("li").removeClass("active"); // $(".minutes-content li[data-time="+minuteDot+"]").addClass("active").prev("li").removeClass("active"); //$(".seconds-content li[data-time='"+secondDot+"']").addClass("active").next("li").removeClass("active"); //为了节省性能,每当秒满60时,才去指向时、分的 if (seconds == 59 || rotateIndexH == 0) { $(".week-wrapper").css("transform", "rotate(" + weekRote + "deg)"); $(".hours-wrapper").css("transform", "rotate(" + hoursRote + "deg)"); $(".minutes-wrapper").css("transform", "rotate(" + minutesRote + "deg)"); //给当前时间的节点添加active 类 (这里 练一下选择器所以写成了一句。) $(".week-content li[data-time=" + weekDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); $(".hours-content li[data-time=" + hourDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); $(".minutes-content li[data-time=" + minuteDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); } // 秒时每秒都需要更新的 $(".seconds-content li[data-time=" + secondDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); // 旋转圈数加一(解决360°转到0°时的bug) hours === 11 ? rotateIndexH++ : 0; minutes === 59 ? rotateIndexM++ : 0; seconds === 59 ? rotateIndexS++ : 0; // 让函数一秒钟执行一次 var timer = setTimeout(run, 1000); }
具体都做了详细的注释,这里就不多提,有个小问题,
$(".seconds-content li[data-time='"+secondDot+"']").addClass("active").next("li").removeClass("active");
不能获取,有人知道原因吗。。。。
欢迎告知。
CSS部分就不多说了,看代码:
*{ padding: 0; margin: 0; color: #eee; user-select: none; } body { background: #bebebe; background-image: url(../images/background2.jpg); background-repeat: no-repeat; background-position:top; background-attachment:fixed; } ul { list-style: none; } li { position: absolute; display: inline-block; } .wrapper { overflow: hidden; height: 100vh; box-sizing: border-box; padding-top: 50px; } .timeBox { margin: 5px auto; width: 800px; height: 800px; /* background: #ccc; */ position: relative; } .wrapper .workTitle { color: #878787; font-family: Arial, Helvetica, sans-serif; text-align: center; line-height: 2em; letter-spacing: .3em; } .wrapper .one-text { color: #dbdbdb; text-align: center; font-size: 18px; font-style: italic; text-shadow: 8px 7px 4px rgba(100,150,200,0.8); letter-spacing: .2em; } .timeBox .week-wrapper { transition: 1s; position: absolute; width: 200px; height: 200px; top: 300px; left: 300px; z-index: 9; } .timeBox .week-wrapper .week-content li { width: 50px; } .timeBox .hours-wrapper { transition: ease 1s; position: absolute; width: 350px; height: 350px; top: 225px; left: 225px; z-index: 8; } .timeBox .hours-wrapper .hours-content li { width: 50px; } .timeBox .minutes-wrapper { transition: 1s; position: absolute; width: 500px; height: 500px; top: 150px; left: 150px; z-index: 7; } .timeBox .minutes-wrapper .minutes-content li { width: 50px; } .timeBox .seconds-wrapper { transition: all 1s linear; position: absolute; width: 650px; height: 650px; top: 75px; left: 75px; z-index: 6; } .timeBox .seconds-wrapper .seconds-content li { transition: .5s; width: 50px; } .timeBox .year-wrapper { width: 80px; height: 80px; border-radius: 50%; position: absolute; top:50%; left: 50%; transform: translate(-50%,-50%); cursor: pointer; z-index: 10; } .timeBox .year-wrapper p { text-align: center; line-height: 80px; font-size: 18px; font-weight: bold; } .timeBox .year-wrapper::after { content:""; display: block; width: 300px; height: 26px; /* background:rgba(139,90,43); */ /* border:1px solid #aaa; border-radius: 3px; */ position: absolute; top:27px; left: 80px; } .active { /* color: rgb(139,90,43); */ color: #FF7F00; } @media screen and (max-width:700px) { .wrapper .timeBox { transform: scale(0.5,0.5) translate(-50%,-50%); margin: 0 auto; } .wrapper .timeBox ul li { font-size:16px; } }
最后,附上完整的代码;
var rotateIndexH = 0; var rotateIndexM = 0; var rotateIndexS = 0; init(); function init() { // 更换背景图片 $(".timeBox .year-wrapper").click(function () { $("body").css("background-image", "url(images/background" + Math.ceil(Math.random() * 4) + ".jpg)") }) // 设置文本值 setNumText(); // 设置干支纪年时间 var nowYear = (new Date).getFullYear(); var acientYear = toAncientYear(nowYear); $(".year-wrapper p").text(acientYear).attr("title", "公元" + nowYear + "农历" + acientYear + "年"); // 初始化内容位置,旋转到指定角度 var weekLen = $(".week-content li").length; var weekDeg = 360 / weekLen; $(".week-content li").each(function (index) { $(this).css({ "transform": "rotate(" + index * weekDeg + "deg)", "transform-origin": "-100% 50%", "margin-left": parseInt($(this).css("width")) * 3 + "px", "margin-top": parseInt($(this).css("width")) * 2 - 10 + "px" }) }) // 时 var hoursLen = $(".hours-content li").length; var hoursDeg = 360 / hoursLen; $(".hours-content li").each(function (index) { $(this).css({ "transform": "rotate(" + index * hoursDeg + "deg)", "transform-origin": "-250% 50%", "margin-left": parseInt($(this).css("width")) * 6 + "px", "margin-top": parseInt($(this).css("width")) * 3.5 - 10 + "px" }) }) //分 var minutesLen = $(".minutes-content li").length; var minutesDeg = 360 / minutesLen; $(".minutes-content li").each(function (index) { $(this).css({ "transform": "rotate(" + index * minutesDeg + "deg)", "transform-origin": "-400% 50%", "margin-left": parseInt($(this).css("width")) * 9 + "px", "margin-top": parseInt($(this).css("width")) * 5 - 10 + "px" }) }) //秒 var secondsLen = $(".seconds-content li").length; var secondsDeg = 360 / secondsLen; $(".seconds-content li").each(function (index) { $(this).css({ "transform": "rotate(" + index * secondsDeg + "deg)", "transform-origin": "-550% 50%", "margin-left": parseInt($(this).css("width")) * 12 + "px", "margin-top": parseInt($(this).css("width")) * 6.5 - 10 + "px" }) }) //每秒刷新一次 run(); } // 设置文本内容 function setNumText() { for (var i = 7; i > 0; i--) { $(".week-content").append("<li data-time = " + i + "> 星期" + numberToZh(i) + "<li>") } for (var i = 12; i > 0; i--) { $(".hours-content").append("<li data-time = " + i + ">" + numberToZh(i) + "时<li>") } for (var i = 60; i > 0; i--) { $(".minutes-content").append("<li data-time = " + i + ">" + numberToZh(i) + "<li>") } for (var i = 60; i > 0; i--) { $(".seconds-content").append("<li data-time = " + i + ">" + numberToZh(i) + "<li>") } } // 数字大小写转换 function numberToZh(num) { var zh = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖", "拾", "佰", "仟", "万"]; var res = ""; if (num <= 10) { res = zh[num]; } else if (num < 20) { var bits = num % 10; res = zh[10] + zh[bits]; } else if (num < 100) { var bits = num % 10; var decade = parseInt(num / 10); if (bits == 0) { res = zh[decade] + zh[10] + "整"; } else { res = zh[decade] + zh[10] + zh[bits]; } } return res; } // 刷新轮盘 function run() { clearInterval(timer); var date = new Date();//获取本地时间 // 分别获取时分秒年 var week = date.getDay(); var hours = date.getHours() % 12; var minutes = date.getMinutes(); var seconds = date.getSeconds(); // 计算对应的旋转角度差 // rotateIndexH 为定义的全局变量,存储了小时的旋转圈数 // rotateIndexM、rotateIndexS 同理为分、秒的 // week var weekRote = 360 / 7 * (week); // hours var hoursRote = 360 / 12 * (hours) + rotateIndexH * 360; // minites secondes var tempDeg = 360 / 60; var minutesRote = tempDeg * (minutes) + rotateIndexM * 360; var secondsRote = tempDeg * (seconds) + rotateIndexS * 360; //var secondsRote = tempDeg * (seconds + 1) + rotateIndexS*360; // 加一是为了滚到位置再变色,去掉会先变色再滚到位置 // 旋转 秒 的位置 $(".seconds-wrapper").css("transform", "rotate(" + secondsRote + "deg)"); // 点亮 由于时间是从0开始计数的所以这里判断 var secondDot = seconds == 0 ? 60 : seconds; var minuteDot = minutes == 0 ? 60 : minutes; var hourDot = hours == 0 ? 60 : hours; var weekDot = hours == 0 ? 7 : week; // $(".hour-content li[data-time="+hourDot+"]").addClass("active").next("li").removeClass("active"); // $(".minutes-content li[data-time="+minuteDot+"]").addClass("active").prev("li").removeClass("active"); //$(".seconds-content li[data-time='"+secondDot+"']").addClass("active").next("li").removeClass("active"); //为了节省性能,每当秒满60时,才去指向时、分的 if (seconds == 59 || rotateIndexH == 0) { $(".week-wrapper").css("transform", "rotate(" + weekRote + "deg)"); $(".hours-wrapper").css("transform", "rotate(" + hoursRote + "deg)"); $(".minutes-wrapper").css("transform", "rotate(" + minutesRote + "deg)"); //给当前时间的节点添加active 类 (这里 练一下选择器所以写成了一句。) $(".week-content li[data-time=" + weekDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); $(".hours-content li[data-time=" + hourDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); $(".minutes-content li[data-time=" + minuteDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); } // 秒时每秒都需要更新的 $(".seconds-content li[data-time=" + secondDot + "]").parents("ul:first").children("li[class='active']").removeClass("active").end().end().addClass("active"); // 旋转圈数加一(解决360°转到0°时的bug) hours === 11 ? rotateIndexH++ : 0; minutes === 59 ? rotateIndexM++ : 0; seconds === 59 ? rotateIndexS++ : 0; // 让函数一秒钟执行一次 var timer = setTimeout(run, 1000); } // 干支纪年法 function toAncientYear(year) { var sky = ["", "辛", "壬", "癸", "甲", "乙", "丙", "丁", "戊", "己", "庚"]; var land = ["", "酉", "戌", "亥", "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申"]; // 用年份除以10得数中余数相对应的便是天干 var one = year % 10; // 用年份除以12得数中余数相对应的便是地支 var two = year % 12; var res = sky[one] + land[two]; return res; } //时间转时辰 function toAncientHours(num) { var res = ""; switch (num) { case 1: res = "子"; break; case 2: res = "丑"; break; case 3: res = "寅"; break; case 4: res = "卯"; break; case 5: res = "辰"; break; case 6: res = "巳"; break; case 7: res = "午"; break; case 8: res = "未"; break; case 9: res = "申"; break; case 10: res = "酉"; break; case 11: res = "戌"; break; case 12: res = "亥"; break; } return res; }
欢迎交流学习。
转载请注明出处,谢谢!
源码地址 提取码:aiml