Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3d96f84

Browse files
feat: 实现canvas绘图简易版
1 parent 217cafd commit 3d96f84

File tree

7 files changed

+281
-4
lines changed

7 files changed

+281
-4
lines changed

‎package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"copy-to-clipboard": "^3.3.3",
2525
"drauu": "^0.3.5",
2626
"driver.js": "^1.2.1",
27+
"gasp": "^0.0.2",
2728
"i18next": "^23.4.6",
2829
"less": "^4.2.0",
2930
"normalize.css": "^8.0.1",
@@ -38,6 +39,7 @@
3839
"redux": "4.2.1",
3940
"redux-persist": "^6.0.0",
4041
"screenfull": "^6.0.2",
42+
"three": "^0.157.0",
4143
"vite": "^4.4.5"
4244
},
4345
"devDependencies": {

‎pnpm-lock.yaml

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/mock/modules/user.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ export const userMock: Record<string, templateOrFn> = {
8080
}
8181
]
8282
},
83+
{
84+
path: '/three',
85+
title: 'threeJs',
86+
icon: 'CalendarOutlined',
87+
children: [
88+
{
89+
path: '/three/car',
90+
title: '3D Car',
91+
icon: 'AppstoreOutlined'
92+
}
93+
]
94+
},
8395
{
8496
path: '/user',
8597
title: '用户管理',
@@ -149,7 +161,8 @@ export const userMock: Record<string, templateOrFn> = {
149161
'commonDrag',
150162
'test',
151163
'drawSvg',
152-
'drawCanvas'
164+
'drawCanvas',
165+
'threeCar'
153166
],
154167
msg: 'success',
155168
code: 200

‎src/router/modules/three.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { lazy } from 'react'
2+
import lazyLoad from '../lazyLoad'
3+
import { RouteObject } from '../interface'
4+
import LayoutContainer from '@/layout'
5+
// 常用组件
6+
const drawRoutes: Array<RouteObject> = [
7+
{
8+
element: <LayoutContainer />,
9+
children: [
10+
{
11+
path: '/three/car',
12+
element: lazyLoad(
13+
lazy(() => import('@/views/three/car/Car')),
14+
{
15+
requiredAuth: true,
16+
title: '3D汽车展示',
17+
code: 'threeCar'
18+
}
19+
)
20+
}
21+
]
22+
}
23+
]
24+
25+
export default drawRoutes

‎src/views/draw/Canvas.tsx

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,196 @@
1-
import React from 'react'
1+
import { Button, Row, Col, ColorPicker } from 'antd'
2+
import React, { useEffect, useMemo, useRef, useState } from 'react'
3+
import styles from './canvas.module.scss'
4+
import { onMove } from '@/utils/util'
25

36
const DrawCanvas = () => {
4-
return <div>DrawCanvas</div>
7+
const canvas = useRef<HTMLCanvasElement>(null)
8+
let [ctx, setCtx] = useState<CanvasRenderingContext2D | null>(null)
9+
const [isDraw, setIsDraw] = useState(false)
10+
const [scaleX, setScaleX] = useState(1)
11+
const [scaleY, setScaleY] = useState(1)
12+
const [activeButton, setActiveButton] = useState('thin')
13+
const [color, setColor] = useState('#000')
14+
let mouseEventRemove = () => {}
15+
function mouseDownHandle(e: React.MouseEvent) {
16+
if (ctx) {
17+
setIsDraw(true)
18+
ctx!.beginPath()
19+
let x = (e.pageX - canvas.current!.offsetLeft) / scaleX
20+
let y = (e.pageY - canvas.current!.offsetTop) / scaleY
21+
ctx!.moveTo(x, y)
22+
mouseEventRemove = onMove(
23+
(e) => {
24+
let x = (e.pageX - canvas.current!.offsetLeft) / scaleX
25+
let y = (e.pageY - canvas.current!.offsetTop) / scaleY
26+
ctx!.lineTo(x, y)
27+
ctx!.stroke()
28+
},
29+
() => {
30+
setIsDraw(false)
31+
ctx!.closePath()
32+
}
33+
)
34+
}
35+
}
36+
// 自适应缩放比
37+
function onresize() {
38+
let width = canvas.current!.offsetWidth
39+
let height = canvas.current!.offsetHeight
40+
setScaleX(width / 800)
41+
setScaleY(height / 600)
42+
}
43+
function init() {
44+
setCtx(() => {
45+
let ctx = canvas.current?.getContext('2d') as CanvasRenderingContext2D
46+
ctx!.lineJoin = 'round'
47+
ctx!.lineCap = 'round'
48+
ctx!.strokeStyle = color
49+
return ctx
50+
})
51+
}
52+
53+
function onBlod() {
54+
ctx!.globalCompositeOperation = 'source-over'
55+
ctx!.lineWidth = 20
56+
setActiveButton('bold')
57+
}
58+
function onThin() {
59+
ctx!.globalCompositeOperation = 'source-over'
60+
ctx!.lineWidth = 1
61+
setActiveButton('thin')
62+
}
63+
function onEraser() {
64+
ctx!.globalCompositeOperation = 'destination-out'
65+
ctx!.lineWidth = 30
66+
setActiveButton('eraser')
67+
}
68+
function onColorChange(color: string) {
69+
setColor(color)
70+
ctx!.strokeStyle = color
71+
}
72+
function onSave() {
73+
setWhiteBackground()
74+
let dataUrl = canvas.current!.toDataURL()
75+
const elem = window.document.createElement('a')
76+
elem.href = dataUrl
77+
elem.download = '签名.png'
78+
elem.click()
79+
}
80+
function onClear() {
81+
ctx!.clearRect(0, 0, canvas.current!.width, canvas.current!.height)
82+
}
83+
function setWhiteBackground() {
84+
// 获取图片数据
85+
let imgData = ctx!.getImageData(
86+
0,
87+
0,
88+
canvas.current!.width,
89+
canvas.current!.height
90+
)
91+
for (let i = 0; i < imgData.data.length; i += 4) {
92+
// 当该像素是透明的,则设置成白色
93+
if (imgData.data[i + 3] == 0) {
94+
imgData.data[i] = 255
95+
imgData.data[i + 1] = 255
96+
imgData.data[i + 2] = 255
97+
imgData.data[i + 3] = 255
98+
}
99+
}
100+
ctx!.putImageData(imgData, 0, 0)
101+
}
102+
useEffect(() => {
103+
if (!canvas.current?.getContext) {
104+
console.log('当前浏览器不支持canvas,请下载最新的浏览器')
105+
}
106+
onresize()
107+
window.addEventListener('resize', onresize)
108+
init()
109+
return () => {
110+
mouseEventRemove()
111+
window.removeEventListener('resize', onresize)
112+
}
113+
}, [])
114+
return (
115+
<div className={'page-container ' + styles.canvasPage}>
116+
<div className={styles.canvasTools}>
117+
<Row align="middle" gutter={4}>
118+
<Col>
119+
<Button
120+
onClick={onBlod}
121+
className={
122+
activeButton == 'bold' ? styles.activeBtn : styles.defaultBtn
123+
}
124+
>
125+
粗线条
126+
</Button>
127+
</Col>
128+
<Col>
129+
<Button
130+
onClick={onThin}
131+
className={
132+
activeButton == 'thin' ? styles.activeBtn : styles.defaultBtn
133+
}
134+
>
135+
细线条
136+
</Button>
137+
</Col>
138+
<Col>
139+
<ColorPicker
140+
// open={colorPickerOpen}
141+
onChange={(color) => onColorChange(color.toHexString())}
142+
value={color}
143+
presets={[
144+
{
145+
label: '快速选择',
146+
colors: [
147+
'#000000',
148+
'#F5222D',
149+
'#FA8C16',
150+
'#FADB14',
151+
'#8BBB11',
152+
'#52C41A',
153+
'#13A8A8',
154+
'#1677FF',
155+
'#2F54EB',
156+
'#722ED1'
157+
]
158+
}
159+
]}
160+
showText
161+
/>
162+
</Col>
163+
<Col>
164+
<Button
165+
onClick={onEraser}
166+
className={
167+
activeButton == 'eraser' ? styles.activeBtn : styles.defaultBtn
168+
}
169+
>
170+
橡皮擦
171+
</Button>
172+
</Col>
173+
<Col>
174+
<Button onClick={onClear}>清除画布</Button>
175+
</Col>
176+
<Col>
177+
<Button onClick={onSave}>保存签名</Button>
178+
</Col>
179+
</Row>
180+
</div>
181+
<canvas
182+
ref={canvas}
183+
width={800}
184+
height={600}
185+
onMouseDown={mouseDownHandle}
186+
className={styles.canvasMain}
187+
>
188+
当前浏览器不支持canvas,请下载最新的浏览器
189+
<a href="https://www.google.cn/chrome/index.html">立即下载</a>
190+
</canvas>
191+
<hr />
192+
</div>
193+
)
5194
}
6195

7196
export default DrawCanvas

‎src/views/draw/canvas.module.scss

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.canvasPage {
2+
display: flex;
3+
flex-direction: column;
4+
}
5+
6+
.canvasTools {
7+
padding: 5px 1.5rem;
8+
border: 1px solid var(--border-color);
9+
position: relative;
10+
padding-right: 30px;
11+
}
12+
13+
.canvasMain {
14+
border: 1px solid var(--border-color);
15+
flex: 1;
16+
height: 0;
17+
cursor: crosshair;
18+
}
19+
20+
.activeBtn {
21+
color: #fff;
22+
background-color: orangered;
23+
border-color: orangered;
24+
}
25+
26+
.defaultBtn {}

‎src/views/three/car/Car.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react'
2+
3+
const Car = () => {
4+
return <div>Car</div>
5+
}
6+
7+
export default Car

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /