<!-- * canvas 曲线 --> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue'; import { drawGrid, rotateOriginOffset, toArc } from '../utils'; const center = { x: 200, y: 300, }; const step = ref(0); const translateX = ref(0); const translateXstep = ref(0); const isShowGrid = ref(false); const state = reactive({ gridDom: null as any, canvasDom: null as any, curveDom: null as any, r: 100, r2: 40, }); onMounted(() => { state.canvasDom = document.getElementById('canvas'); state.gridDom = document.getElementById('canvas-grid'); state.curveDom = document.getElementById('canvas-curve'); initCanvas(); }); function initCanvas() { const { r } = state; let ctx = state.canvasDom.getContext('2d'); let pen = state.curveDom.getContext('2d'); translateXstep.value = (r - Math.cos(toArc(30)) * r) / 60; window.requestAnimationFrame(() => { draw(ctx, r, pen); }); } function draw(ctx: any, r: number, pen: any) { // 基础圆 ctx.save(); ctx.clearRect(0, 0, 1200, 600); ctx.beginPath(); ctx.arc(center.x, center.y, r, 0, 2 * Math.PI); ctx.lineWidth = 1; ctx.strokeStyle = '#78C0A8'; ctx.stroke(); ctx.restore(); ctx.save(); // 每次走0.5度,1秒钟走30度, const arc = toArc(360 - step.value * 0.5); // const arc = toArc(30); const offset1 = rotateOriginOffset(1200, center, arc); ctx.translate(offset1.x, offset1.y); // 改变旋转中心 ctx.rotate(arc); ctx.beginPath(); ctx.moveTo(center.x, center.y); ctx.lineTo(center.x + r, center.y); ctx.lineWidth = 2; ctx.strokeStyle = '#78C0A8'; ctx.stroke(); // 第二个圆 const centerX2 = center.x + r; // 第2个圆的圆心 const centerY2 = center.y; ctx.beginPath(); ctx.arc(centerX2, centerY2, 4, 0, Math.PI * 2); ctx.strokeStyle = '#FC4422'; ctx.stroke(); ctx.restore(); // 圆弧上点的坐标 const a = Math.sin(arc) * r; const b = Math.cos(arc) * r; const x0 = center.x + b; const y0 = center.y + a; const lastX = center.x + r + 100; ctx.save(); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(lastX, y0); ctx.strokeStyle = '#78C0A8'; ctx.stroke(); ctx.restore(); // 曲线画布 translateX.value += translateXstep.value; // 每次平移的值 pen.save(); pen.beginPath(); pen.arc(lastX - translateX.value, y0, 2, 0, Math.PI * 2); pen.fillStyle = '#78C0A8'; pen.fill(); pen.restore(); step.value++; // 回调函数执行次数通常是每秒 60 次 // if (step.value < 60) { // window.requestAnimationFrame(() => { // draw(ctx, r, pen); // }); // } if (translateX.value > 400) { translateX.value = 0; pen.clearRect(0, 0, 1200, 600); } if (step.value === 60 * 12) { step.value = 0; } window.requestAnimationFrame(() => { draw(ctx, r, pen); }); } function showGrid() { let ctx = state.gridDom.getContext('2d'); isShowGrid.value = !isShowGrid.value; if (isShowGrid.value) { drawGrid('red', 1200, 600, ctx); } else { ctx.clearRect(0, 0, 1200, 600); } } </script> <template> <div class="fit"> <div class="title">canvas 曲线</div> <div class="btns"> <q-btn label="网格" color="primary" @click="showGrid" /> </div> <div class="relative-position"> <canvas id="canvas" width="1200" height="600" style="border: 1px solid red; position: absolute; top: 0; left: 0" ></canvas> <canvas id="canvas-curve" width="1200" height="600" :style="{ border: '1px solid transparent', position: 'absolute', top: 0, left: 0, transform: `translate(${translateX}px, 0)`, }" ></canvas> <canvas id="canvas-grid" width="1200" height="600" style=" border: 1px solid transparent; position: absolute; top: 0; left: 0; " ></canvas> </div> </div> </template> <style lang="scss" scoped> .title { margin-bottom: $padding-md; } .btns { margin-bottom: $padding-md; } </style>