캔버스 시계 인터페이스를 그리고 타이머를 사용하여 전체 캔버스를 정기적으로 다시 그리면 동적 시계를 시뮬레이션하는 효과를 얻을 수 있습니다.
어려움은 다음과 같습니다.
두 번째 눈금과 시계 눈금의 그림
시간별 텍스트는 안쪽 가장자리를 따라 원으로 둘러싸여 있습니다.
눈금의 포장은 그렇지 않습니다. 계산하기 어렵고 텍스트 배치가 조금 더 까다롭습니다. 캔버스에 그려진 텍스트는 그리기 좌표 위에 있습니다(텍스트 기준선 및 정렬의 영향을 받음). 텍스트의 중간점이 되도록 오프셋을 계산해야 합니다. 정확히 원에 들어갑니다.
API에서 문자 높이를 측정할 수 있는 방법이 없고, 실제로는 글꼴 크기를 사용하는 것이 문자의 정확한 높이가 아니기 때문에 이 단계는 매우 간단합니다. 따라서 y 좌표 + 줄 높이의 절반을 아래쪽으로 오프셋하여 만듭니다. 수직이지만 항상 정확하지는 않습니다. x좌표 + 선폭의 절반이 왼쪽으로 오프셋되어 가로 중앙에 배치되지만 API에서 선폭을 측정하는 방법을 제공하므로 이러한 문제는 없습니다.
다 ctx.measureText(text).width
存在,但 ctx.measureText(numText).height
가 존재하지 않기 때문입니다. 인쇄 측정 결과에도 너비 속성이 하나만 있습니다. 문서에는 캔버스의 텍스트 그리기 지원이 상대적으로 약하다고 나와 있습니다.
기준선과 정렬을 직접 중앙으로 설정하다보니 약간의 오차가 있는 것 같고 항상 눈에 좋아 보이지는 않습니다. 두 가지 방법 모두 아래 코드에 작성되어 있습니다.
걷는 시계 미리보기:
시간 표시에 약간의 오류가 있을 수 있습니다.
주요 지식 포인트는 원의 좌표식과 삼각 함수 sin 및 cos의 계산입니다. 실제로 원의 좌표식은 많이 사용되지 않으며, 평가를 도입하면 복잡해질 수 있습니다.
다음 그림은 눈금 좌표 및 전체 점 텍스트 그리기 좌표 계산을 위한 참고 그림입니다.
다음은 전체 코드입니다.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=1024, height=768,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <title>时钟</title></head><body><p style="margin: 50px"> <canvas width="300" height="300"></canvas> <canvas width="200" height="200" style="background-color: #bbbbbb"></canvas> <canvas width="200" height="200"></canvas> <canvas width="200" height="200"></canvas></p><script> var clockHelper = function (canvas, config) { if (!config) { config = {} } var ctx = canvas.getContext('2d'); var deColor = config.deColor ? config.deColor : '#333333'; var deConfig = { ringWidth: config.ringWidth ? config.ringWidth : 6,//圆环宽度 ringColor: config.ringColor ? config.ringColor : deColor,//圆环颜色 hSaleL: config.hSaleL ? config.hSaleL : 8,//时刻度线长 hScaleWidth: config.hScaleWidth ? config.hScaleWidth : 4,//时刻度线宽 hScaleColor: config.hScaleColor ? config.hScaleColor : deColor,//时刻度颜色 msSaleL: config.msSaleL ? config.msSaleL : 4,//分秒刻度线长 msScaleWidth: config.msScaleWidth ? config.msScaleWidth : 2,//分秒刻度线宽 msScaleColor: deColor,//分秒刻度颜色 hFontSize: config.hFontSize ? config.hFontSize : 18,//整点文字大小 hHandWidth: config.hHandWidth ? config.hHandWidth : 10,//时针宽度 mHandWidth: config.mHandWidth ? config.mHandWidth : 5,//分针宽度 sHandWidth: config.sHandWidth ? config.sHandWidth : 2,//秒针宽度 hHandColor: config.hHandColor ? config.hHandColor : deColor,//时针颜色 mHandColor: config.mHandColor ? config.mHandColor : deColor,//分针颜色 sHandColor: config.sHandColor ? config.sHandColor : '#bb3333',//秒针颜色 handMode: ['ms', 's'].indexOf("" + config.handMode) !== -1 ? config.handMode : 's',//指针读秒模式,ms:毫秒,s:秒。 clockFaceColor: config.clockFaceColor ? config.clockFaceColor : '', }; var ox = canvas.width / 2; var oy = canvas.height / 2; var width = canvas.width; var height = canvas.height; ctx.font = deConfig.hFontSize + "px 黑体"; //中线圆环半径 var ringR = (width < height) ? (width / 2 - deConfig.ringWidth / 2) : (height / 2 - deConfig.ringWidth / 2); //内圆环半径 var ringInnerR = (width < height) ? (width / 2 - deConfig.ringWidth) : (height / 2 - deConfig.ringWidth); var timer; var timeSleep = 100; var isStart = false; function start() { if (isStart) { return; } isStart = true; if (deConfig.handMode == 'ms') { timeSleep = 100; } else { timeSleep = 1000; } ctx.clearRect(0, 0, width, height); draw(); timer = setInterval(function () { if (isStart) { ctx.clearRect(0, 0, width, height); draw(); } }, timeSleep); } function stop() { isStart = false; clearInterval(timer) } function draw() { beforeDraw(); drawCircleFace(); drawHands(); afterDraw(); } function drawCircleFace() { ctx.fillStyle = deConfig.ringColor; ctx.strokeStyle = deConfig.ringColor; ctx.lineWidth = deConfig.ringWidth; ctx.beginPath(); ctx.arc(ox, oy, ringR, 0, Math.PI * 2); ctx.stroke(); if (deConfig.clockFaceColor) { ctx.fillStyle = deConfig.clockFaceColor; ctx.fill(); } var x1 = ox; var y1 = oy; var x2 = ox; var y2 = oy; var radin = 0; ctx.lineWidth = deConfig.hScaleWidth; // ctx.beginPath(); for (var i = 1; i <= 60; i++) { radin = i * 6 * Math.PI / 180; x1 = ox + ringInnerR * Math.sin(radin); y1 = oy - ringInnerR * Math.cos(radin); if (i % 5 === 0) { ctx.lineWidth = deConfig.hScaleWidth; x2 = ox + (ringInnerR - deConfig.hSaleL) * Math.sin(radin); y2 = oy - (ringInnerR - deConfig.hSaleL) * Math.cos(radin); ctx.fillStyle = deConfig.hScaleColor; var numText = i / 5 + ""; var textWidth = ctx.measureText(numText).width; var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin); var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; //不设置文字居中,基线居中,自己计算。貌似都有误差。因为旋转过程中,角度变化,且文字宽高不尽相同 // var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin) - textWidth / 2; // var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin) + deConfig.hFontSize/ 2; //x2,y2已经求过,化简为: // var x3 = x2 - deConfig.hFontSize * Math.sin(radin) - textWidth / 2; // var y3 = y2 + deConfig.hFontSize * Math.cos(radin) + textWidth / 2; //文字x轴向左偏移一半文字宽,使之水平居中;向下偏移一半高度,使之垂直居中。 // 实际中发现,字高没法测(api无),而使用fontSize不准。但y轴加上字宽,位置倒是更对齐一些。 // var x3 = x2 + textWidth / 2; // var y3 = y2 - deConfig.hFontSize / 2; ctx.fillText(numText, x3, y3); } else { ctx.lineWidth = deConfig.msScaleWidth; x2 = ox + (ringInnerR - deConfig.msSaleL) * Math.sin(radin); y2 = oy - (ringInnerR - deConfig.msSaleL) * Math.cos(radin); } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); } } //改变坐标中点,并旋转画布也许是更好的选择。 function drawHands() { var date = new Date(); var h = date.getHours() % 12; var m = date.getMinutes(); var s = date.getSeconds(); var ms = date.getMilliseconds(); // console.log(h + ":" + m + ":" + s); // 时针 var hRadin = (h + m / 60 + s / 3600) * Math.PI * 2 / 12; var mRadin = (m + s / 60) * Math.PI * 2 / 60; var sRadin; if (deConfig.handMode == 'ms') { sRadin = (s + ms / 1000) * Math.PI * 2 / 60; } else { sRadin = s * Math.PI * 2 / 60; } var x = 0; var y = 0; var hDotR = deConfig.hHandWidth + 2; var mDotR = 0.6 * hDotR var sDotR = 0.5 * hDotR //秒针半径 var sHandR = ringInnerR - deConfig.hSaleL * 2 //分针半径 var mHandR = 0.8 * sHandR; //时针半径 var hHandR = 0.7 * mHandR; //时针 ctx.beginPath(); ctx.lineWidth = deConfig.hHandWidth; ctx.strokeStyle = deConfig.hHandColor; ctx.strokeStyle = deConfig.hHandColor; ctx.moveTo(ox, oy); x = ox + hHandR * Math.cos(hRadin - Math.PI / 2); y = oy + hHandR * Math.sin(hRadin - Math.PI / 2); ctx.lineTo(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginPath(); ctx.lineWidth = 0; ctx.arc(x, y, deConfig.hHandWidth / 2, 0, 2 * Math.PI); ctx.fill(); //中心 ctx.beginPath(); // ctx.lineWidth = hDotR; ctx.arc(ox, oy, hDotR / 2, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); //分针 ctx.beginPath(); ctx.lineWidth = deConfig.mHandWidth; ctx.strokeStyle = deConfig.mHandColor; ctx.fillStyle = deConfig.mHandColor; ctx.moveTo(ox, oy); x = ox + mHandR * Math.cos(mRadin - Math.PI / 2); y = oy + mHandR * Math.sin(mRadin - Math.PI / 2); ctx.lineTo(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginPath(); ctx.lineWidth = 0; ctx.arc(x, y, deConfig.mHandWidth / 2, 0, 2 * Math.PI); ctx.fill(); //中心 ctx.beginPath(); ctx.arc(ox, oy, mDotR / 2, 0, Math.PI * 2); ctx.stroke(); //秒针 ctx.beginPath(); ctx.strokeStyle = deConfig.sHandColor; ctx.fillStyle = deConfig.sHandColor; ctx.lineWidth = deConfig.sHandWidth; //秒针有长短两线 x = ox - sHandR / 4 * Math.cos(sRadin - Math.PI / 2); y = oy - sHandR / 4 * Math.sin(sRadin - Math.PI / 2); ctx.moveTo(x, y); x = ox + sHandR * Math.cos(sRadin - Math.PI / 2); y = oy + sHandR * Math.sin(sRadin - Math.PI / 2); ctx.lineTo(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginPath(); ctx.lineWidth = 0; ctx.arc(x, y, deConfig.sHandWidth / 2, 0, 2 * Math.PI); ctx.fill(); //中心 ctx.beginPath(); ctx.fillStyle = deColor; ctx.arc(ox, oy, sDotR, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } function beforeDraw() { if (typeof exp.beforeDraw === 'function') { exp.beforeDraw(ctx, deConfig); } } function afterDraw() { if (typeof exp.afterDraw === 'function') { exp.afterDraw(ctx, deConfig); } } var exp = { start: start, stop: stop, beforeDraw: null, afterDraw: null, } return exp; } var clockCanvas1 = document.getElementsByTagName('canvas')[0]; var clockCanvas2 = document.getElementsByTagName('canvas')[1]; var clockCanvas3 = document.getElementsByTagName('canvas')[2]; var clockCanvas4 = document.getElementsByTagName('canvas')[3]; var clock = clockHelper(clockCanvas1, {mHandColor: '#3333bb', sHandColor: '#bb3333'}); clock.start(); setTimeout(function () { clock.stop() }, 5000) setTimeout(function () { clock.start(); }, 8000) clockHelper(clockCanvas2, { mHandColor: 'green', hHandWidth: 6, mHandWidth: 4, hFontSize: 14, hScaleWidth: 2, handMode: 'ms' }).start(); clockHelper(clockCanvas2, { mHandColor: 'green', hHandWidth: 6, mHandWidth: 4, hFontSize: 14, hScaleWidth: 2, handMode: 'ms' }).start(); clockHelper(clockCanvas3, { deColor: '#bbbbbb', sHandColor: '#bbbbbb', clockFaceColor: '#112233',//钟面 hHandWidth: 6, mHandWidth: 4, hFontSize: 14, hScaleWidth: 2, handMode: 's' }).start(); var clock4 = clockHelper(clockCanvas4, { deColor: '#bbbbbb', sHandColor: '#bbbbbb', // clockFaceColor: '#112233', hHandWidth: 6, mHandWidth: 4, hFontSize: 14, hScaleWidth: 2, handMode: 's' }); clock4.afterDraw = function (ctx, deConfig) { var grd = ctx.createLinearGradient(0, 0, clockCanvas4.width, clockCanvas4.height); grd.addColorStop(0, "rgba(255,0,0,0.3)"); grd.addColorStop(1, "rgba(0,0,255,0.5)"); ctx.fillStyle = grd; ctx.arc(clockCanvas4.width/2,clockCanvas4.height/2,clockCanvas4.width/2,0,Math.PI*2); // ctx.fillRect(0, 0, clockCanvas4.width, clockCanvas4.height); ctx.fill(); ctx.fillText('时钟绘制完成后,自定义其他绘制',clockCanvas4.width/2,clockCanvas4.height - deConfig.hFontSize); }; clock4.start();</script></body></html>
지침:
1 . clockHelper 매개변수가 캔버스로 전달됩니다. 두 번째 매개변수는 포인터의 색상과 크기, 눈금 등을 포함하여 시계 인터페이스의 구성 개체로 전달됩니다. 구성 항목은 clockHelper의 deConfig 기본 개체를 기준으로 합니다. deConfig의 속성
2. clockHelper의 캡슐화 상태가 약간 좋지 않아 기본적으로만 사용할 수 있습니다. 하지만 속성이 많지 않으니 변신이 어렵지는 않을 것 같습니다.
3. 시계 인터페이스를 그리기 전과 후에 메소드를 제공합니다. beforeDraw 및 afterDraw 두 가지 메소드에서 자체 로직을 실행할 수 있습니다. 그러나 사전 설계에서는 충분한 여백 픽셀을 남기지 않았기 때문에 거의 쓸모가 없습니다. 간단한 재도색만 가능합니다. 예를 들어 시계 문자판에 색상과 그라데이션을 추가합니다.
관련 권장 사항:
위 내용은 h5 캔버스를 사용하여 시계의 동적 효과 얻기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!