参考链接:https://codepen.io/luren/pen/yEagYO
画布
<canvas
id="rule"
height="40"
width="1020"
style={{ position: 'absolute' }}></canvas>
配置项
const [config, setConfig] = useState({
size: 101, // 刻度尺总刻度数,(100个间隔需要101个刻度线)
x: 0, // 刻度尺x坐标位置,单位px
y: 0, // 刻度尺y坐标位置,单位px
w: 10, // 刻度线的间隔,单位px
h: 10, // 刻度线基础长度,单位px
width: 1020, // 刻度尺宽度(写长20px是为了最后一个刻度值能被显示出来)
height: 40, // 刻度尺高度
});
画刻度方法
const draw = (cxt: CanvasRenderingContext2D, config: configVo) => {
const size = config.size || 101;
const x = config.x || 0;
const y = config.y || 0;
const w = config.w || 10;
const h = config.h || 10;
let offset = 3; // 数字的偏移量
// 画之前清空画布
cxt.clearRect(0, 0, config.width, config.height);
// 设置画笔属性
cxt.strokeStyle = '#aaa'; // 画笔颜色
cxt.lineWidth = 1; // 画笔大小
cxt.font = '12'; //字体大小
// 画刻度线
for (let i = 0; i < size; i++) {
// 开始一条路径
cxt.beginPath();
// 移动到指定位置
cxt.moveTo(x + i * w, y);
// 满10刻度时刻度线长一些 并且在上方表明刻度
if (i % 10 == 0) {
// 计算偏移量
offset = (String(i / 10).length * 6) / 2; // 数字的长度为:String(i / 10).length, /2 是为了居中
cxt.fillText(i + '', x + i * w, y + h * 3.5); // 数字
cxt.lineTo(x + i * w, y + h * 2); // 刻度线长(2h)
} else {
// 满5刻度(刻度线长:1.5h)时的刻度线略长于1刻度(刻度线长1h)的
cxt.lineTo(x + i * w, y + (i % 5 === 0 ? 1.5 : 1) * h);
}
// 画出路径
cxt.stroke();
}
};
初始化
useEffect(() => {
const canvas = document.getElementById('rule') as HTMLCanvasElement;
if (!canvas) return;
const cxt = canvas.getContext('2d') as CanvasRenderingContext2D;
draw(cxt, config);
}, [config]);
完整代码

import { useState, useEffect } from 'react';
interface configVo {
size: number;
x: number;
y: number;
w: number;
h: number;
width: number;
height: number;
}
const Rule = () => {
const [config, setConfig] = useState({
size: 101, // 刻度尺总刻度数,(100个间隔需要101个刻度线)
x: 0, // 刻度尺x坐标位置,单位px
y: 0, // 刻度尺y坐标位置,单位px
w: 10, // 刻度线的间隔,单位px
h: 10, // 刻度线基础长度,单位px
width: 1020, // 刻度尺宽度(写长20px是为了最后一个刻度值能被显示出来)
height: 40, // 刻度尺高度
});
const draw = (cxt: CanvasRenderingContext2D, config: configVo) => {
const size = config.size || 101;
const x = config.x || 0;
const y = config.y || 0;
const w = config.w || 10;
const h = config.h || 10;
let offset = 3; // 数字的偏移量
// 画之前清空画布
cxt.clearRect(0, 0, config.width, config.height);
// 设置画笔属性
cxt.strokeStyle = '#aaa'; // 画笔颜色
cxt.lineWidth = 1; // 画笔大小
cxt.font = '12'; //字体大小
// 画刻度线
for (let i = 0; i < size; i++) {
// 开始一条路径
cxt.beginPath();
// 移动到指定位置
cxt.moveTo(x + i * w, y);
// 满10刻度时刻度线长一些 并且在上方表明刻度
if (i % 10 == 0) {
// 计算偏移量
offset = (String(i / 10).length * 6) / 2; // 数字的长度为:String(i / 10).length, /2 是为了居中
cxt.fillText(i + '', x + i * w, y + h * 3.5); // 数字
cxt.lineTo(x + i * w, y + h * 2); // 刻度线长(2h)
} else {
// 满5刻度(刻度线长:1.5h)时的刻度线略长于1刻度(刻度线长1h)的
cxt.lineTo(x + i * w, y + (i % 5 === 0 ? 1.5 : 1) * h);
}
// 画出路径
cxt.stroke();
}
};
useEffect(() => {
const canvas = document.getElementById('rule') as HTMLCanvasElement;
if (!canvas) return;
const cxt = canvas.getContext('2d') as CanvasRenderingContext2D;
draw(cxt, config);
}, [config]);
return (
<canvas
id="rule"
height="40"
width="1020"
style={{ position: 'absolute' }}></canvas>
);
};
export default Rule;
监听窗口变化(窗口宽度均分为100份)
1.使用width变量存储窗口宽度
const [width, setWidth] = useState(0);
2.监听窗口变化
useEffect(() => {
getWrapperSize();
window.addEventListener('resize', getWrapperSize);
return () => {
window.removeEventListener('resize', getWrapperSize);
};
}, [getWrapperSize]);
const getWrapperSize = useCallback(() => {
const canvas = document.getElementById('rule');
if (!canvas) return;
setConfig({
...config,
w: window.innerWidth / 100,
});
canvas.setAttribute('width', window.innerWidth + 'px');
setWidth(window.innerWidth);
}, [config]);
3.当config变化/width变化,都需要重新绘制尺子
useEffect(() => {
const canvas = document.getElementById('rule') as HTMLCanvasElement;
if (!canvas) return;
const cxt = canvas.getContext('2d') as CanvasRenderingContext2D;
draw(cxt, config);
}, [width, config]);
完整代码如下:此时窗口大小变化,尺子大小也会发生变化


import { useState, useCallback, useEffect } from 'react';
interface configVo {
size: number;
x: number;
y: number;
w: number;
h: number;
width: number;
height: number;
}
const Rule = () => {
const [width, setWidth] = useState(0);
const [config, setConfig] = useState({
size: 101, // 刻度尺总刻度数,(100个间隔需要101个刻度线)
x: 0, // 刻度尺x坐标位置,单位px
y: 0, // 刻度尺y坐标位置,单位px
w: 10, // 刻度线的间隔,单位px
h: 10, // 刻度线基础长度,单位px
width: width, // 刻度尺宽度(写长20px是为了最后一个刻度值能被显示出来)
height: 40, // 刻度尺高度
});
const draw = (cxt: CanvasRenderingContext2D, config: configVo) => {
const size = config.size || 101;
const x = config.x || 0;
const y = config.y || 0;
const w = config.w || 10;
const h = config.h || 10;
let offset = 3; // 数字的偏移量
// 画之前清空画布
cxt.clearRect(0, 0, config.width, config.height);
// 设置画笔属性
cxt.strokeStyle = '#aaa'; // 画笔颜色
cxt.lineWidth = 1; // 画笔大小
cxt.font = '12'; //字体大小
// 画刻度线
for (let i = 0; i < size; i++) {
// 开始一条路径
cxt.beginPath();
// 移动到指定位置
cxt.moveTo(x + i * w, y);
// 满10刻度时刻度线长一些 并且在上方表明刻度
if (i % 10 == 0) {
// 计算偏移量
offset = (String(i / 10).length * 6) / 2; // 数字的长度为:String(i / 10).length, /2 是为了居中
cxt.fillText(i + '', x + i * w, y + h * 3.5); // 数字
cxt.lineTo(x + i * w, y + h * 2); // 刻度线长(2h)
} else {
// 满5刻度(刻度线长:1.5h)时的刻度线略长于1刻度(刻度线长1h)的
cxt.lineTo(x + i * w, y + (i % 5 === 0 ? 1.5 : 1) * h);
}
// 画出路径
cxt.stroke();
}
};
const getWrapperSize = useCallback(() => {
const canvas = document.getElementById('rule');
if (!canvas) return;
setConfig({
...config,
w: window.innerWidth / 100,
});
canvas.setAttribute('width', window.innerWidth + 'px');
setWidth(window.innerWidth);
}, []);
useEffect(() => {
setTimeout(() => {
getWrapperSize();
}, 100);
window.addEventListener('resize', getWrapperSize);
return () => {
window.removeEventListener('resize', getWrapperSize);
};
}, []);
useEffect(() => {
const canvas = document.getElementById('rule') as HTMLCanvasElement;
if (!canvas) return;
const cxt = canvas.getContext('2d') as CanvasRenderingContext2D;
draw(cxt, config);
}, [width, config]);
return (
<canvas
id="rule"
height="40"
width="1020"
style={{ position: 'absolute' }}></canvas>
);
};
export default Rule;
使用React和Canvas动态绘制响应式刻度尺
该代码示例展示了如何在React应用中使用CanvasAPI创建一个可自定义的刻度尺,并使其根据窗口大小变化调整刻度间隔。通过监听窗口尺寸,动态计算并更新Canvas的宽度以及刻度线的间隔,确保刻度尺始终均匀分布。
4657






