@ray-js/graffiti 1.0.0 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,8 +31,10 @@ yarn start:tuya
31
31
  - By changing the operation type, you can switch to pencil mode, eraser mode, paint bucket mode
32
32
  - By changing `penColor`, you can pass in different pen colors
33
33
  - `needStroke` is `true`, start monitoring each brush data, and get the x, y coordinate path data of each brush stroke through `onStrokeChange`
34
- - By updating the value of `saveTrigger`, you can trigger canvas saving and return the base64 data of the canvas
35
- - By updating the value of `clearTrigger`, you can trigger canvas clearing
34
+ - Updating the `saveTrigger` value triggers canvas saving, returning the canvas's base64 data and grid color data.
35
+ - Updating the `clearTrigger` value triggers canvas clearing.
36
+ - The `scale` function allows scaling the canvas.
37
+ - `isDragging` disables drawing; the canvas can be dragged when it exceeds the limit.
36
38
 
37
39
  ```tsx
38
40
  import React, { useState } from 'react';
@@ -1,5 +1,5 @@
1
1
  import Render from './index.rjs';
2
- import { getSystemInfoSync } from '@ray-js/ray';
2
+ import { getSystemInfoSync, getElementById, getBoundingClientRect } from '@ray-js/ray';
3
3
  let _systemInfoResult = null;
4
4
  export const getSystemInfoResult = () => {
5
5
  if (_systemInfoResult) {
@@ -127,6 +127,35 @@ Component({
127
127
  this.render.clear();
128
128
  }
129
129
  }
130
+ },
131
+ scale: {
132
+ // 画布缩放比例
133
+ type: Number,
134
+ value: 1,
135
+ observer(newValue) {
136
+ if (newValue && this.render) {
137
+ this.render.updateScale(newValue);
138
+ }
139
+ }
140
+ },
141
+ isDragging: {
142
+ // 可拖动画布, 同时禁止绘制
143
+ type: Boolean,
144
+ value: false,
145
+ observer(newValue) {
146
+ if (this.render) {
147
+ this.render.setDragging(newValue);
148
+ }
149
+ }
150
+ },
151
+ drawData: {
152
+ // 初始绘制数据
153
+ type: null,
154
+ observer(newValue) {
155
+ if (newValue && this.render) {
156
+ this.render.drawData(newValue);
157
+ }
158
+ }
130
159
  }
131
160
  },
132
161
  data: {
@@ -137,7 +166,7 @@ Component({
137
166
  created() {
138
167
  this.render = new Render(this);
139
168
  },
140
- ready() {
169
+ async ready() {
141
170
  let {
142
171
  canvasIdPrefix,
143
172
  width,
@@ -147,7 +176,9 @@ Component({
147
176
  pixelSizeX,
148
177
  pixelSizeY,
149
178
  pixelGap,
150
- mode
179
+ mode,
180
+ scale,
181
+ isDragging
151
182
  } = this.data;
152
183
  width = getDeviceRealPx(width);
153
184
  height = getDeviceRealPx(height);
@@ -171,6 +202,8 @@ Component({
171
202
  realHeight: height
172
203
  });
173
204
  }
205
+ const ele = await getElementById(`${canvasIdPrefix}-container`);
206
+ const rect = await getBoundingClientRect(ele);
174
207
  this.render.initPanel({
175
208
  canvasIdPrefix: canvasIdPrefix,
176
209
  width: width,
@@ -183,8 +216,14 @@ Component({
183
216
  pixelGap: pixelGap,
184
217
  pixelShape: this.data.pixelShape,
185
218
  pixelColor: this.data.pixelColor,
186
- penColor: this.data.penColor
219
+ penColor: this.data.penColor,
220
+ scale: scale,
221
+ isDragging: isDragging,
222
+ boxRect: rect
187
223
  });
224
+ if (this.data.drawData) {
225
+ this.render.drawData(this.data.drawData);
226
+ }
188
227
  }
189
228
  },
190
229
  methods: {
@@ -0,0 +1,6 @@
1
+ .pixelGraffiti_container {
2
+ overflow: hidden;
3
+ display: flex;
4
+ justify-content: center;
5
+ align-items: center;
6
+ }
@@ -14,7 +14,13 @@ export default Render({
14
14
  pixelShape,
15
15
  pixelColor,
16
16
  penColor,
17
+ scale,
18
+ isDragging,
19
+ boxRect,
17
20
  }) {
21
+ this.scale = scale || 1;
22
+ this.isDragging = isDragging || false;
23
+ this.boxRect = boxRect || null;
18
24
  let canvas = await getCanvasById(`${canvasIdPrefix}-sourceCanvas`);
19
25
 
20
26
  // 根据屏幕分辨率动态计算canvas尺寸
@@ -54,9 +60,23 @@ export default Render({
54
60
  // 记录触摸是否开始
55
61
  this.isTouchStarted = false;
56
62
 
63
+ this.gridData = {};
64
+
65
+ // 缩放拖动相关
66
+ this.offsetX = 0;
67
+ this.offsetY = 0;
68
+ this.lastX = 0;
69
+ this.lastY = 0;
70
+ // 计算缩放后格子尺寸
71
+ this.scalePixelSizeX = this.pixelSizeX * scale;
72
+ this.scalePixelSizeY = this.pixelSizeY * scale;
73
+ this.scalePixelGap = this.pixelGap * scale;
74
+
57
75
  // 初始化画布, 绘制像素点
58
76
  this.createPixel(pixelColor);
59
77
 
78
+ this.updateScale(scale);
79
+
60
80
  canvas.addEventListener('touchstart', this.handleTouchstart, false);
61
81
  canvas.addEventListener('touchmove', this.handleTouchmove, false);
62
82
  canvas.addEventListener('touchend', this.handleTouchend, false);
@@ -65,13 +85,32 @@ export default Render({
65
85
  ctx.imageSmoothingQuality = 'high'; // 高质量抗锯齿
66
86
  },
67
87
  handleTouchstart(e) {
68
- const { canvas, pixelGap, pixelSizeX, pixelSizeY } = this;
88
+ const {
89
+ canvas,
90
+ pixelGap,
91
+ pixelSizeX,
92
+ pixelSizeY,
93
+ isDragging,
94
+ scalePixelSizeX,
95
+ scalePixelSizeY,
96
+ scalePixelGap,
97
+ } = this;
98
+ if (isDragging) {
99
+ const touch = e.changedTouches[0];
100
+ this.lastX = touch.pageX;
101
+ this.lastY = touch.pageY;
102
+ return;
103
+ }
69
104
  this.touchedSquaresSet.clear();
70
105
  this.isTouchStarted = true;
71
106
  const touch = e.changedTouches[0];
72
107
  const rect = canvas.getBoundingClientRect();
73
- const x = Math.floor((touch.pageX - rect.left - pixelGap) / (pixelSizeX + pixelGap));
74
- const y = Math.floor((touch.pageY - rect.top - pixelGap) / (pixelSizeY + pixelGap));
108
+ const x = Math.floor(
109
+ (touch.pageX - rect.left - scalePixelGap) / (scalePixelSizeX + scalePixelGap)
110
+ );
111
+ const y = Math.floor(
112
+ (touch.pageY - rect.top - scalePixelGap) / (scalePixelSizeY + scalePixelGap)
113
+ );
75
114
  const coordinate = `${x},${y}`;
76
115
  if (!this.touchedSquaresSet.has(coordinate)) {
77
116
  this.touchedSquaresSet.add(coordinate);
@@ -79,13 +118,58 @@ export default Render({
79
118
  }
80
119
  },
81
120
  handleTouchmove(e) {
82
- const { canvas, pixelGap, pixelSizeX, pixelSizeY } = this;
121
+ const {
122
+ canvas,
123
+ pixelGap,
124
+ pixelSizeX,
125
+ pixelSizeY,
126
+ scalePixelGap,
127
+ scalePixelSizeX,
128
+ scalePixelSizeY,
129
+ } = this;
83
130
  e.preventDefault();
131
+ if (this.isDragging) {
132
+ const touch = e.changedTouches[0];
133
+ const rect = canvas.getBoundingClientRect();
134
+ const dx = touch.pageX - this.lastX;
135
+ const dy = touch.pageY - this.lastY;
136
+ this.offsetX += dx;
137
+ this.offsetY += dy;
138
+ this.lastX = touch.pageX;
139
+ this.lastY = touch.pageY;
140
+
141
+ // 获取容器大小
142
+ const boxWidth = this.boxRect?.width;
143
+ const boxHeight = this.boxRect?.height;
144
+
145
+ // 缩放后的画布内容尺寸
146
+ const displayWidth = rect.width;
147
+ const displayHeight = rect.height;
148
+ // 边界限制
149
+ if (displayWidth <= boxWidth) {
150
+ this.offsetX = 0;
151
+ } else {
152
+ const maxOffsetX = (displayWidth - boxWidth) / 2;
153
+ this.offsetX = Math.max(-maxOffsetX, Math.min(maxOffsetX, this.offsetX));
154
+ }
155
+ if (displayHeight <= boxHeight) {
156
+ this.offsetY = 0;
157
+ } else {
158
+ const maxOffsetY = (displayHeight - boxHeight) / 2;
159
+ this.offsetY = Math.max(-maxOffsetY, Math.min(maxOffsetY, this.offsetY));
160
+ }
161
+ canvas.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.scale})`;
162
+ return;
163
+ }
84
164
  if (this.isTouchStarted) {
85
165
  const touch = e.changedTouches[0];
86
166
  const rect = canvas.getBoundingClientRect();
87
- const x = Math.floor((touch.pageX - rect.left - pixelGap) / (pixelSizeX + pixelGap));
88
- const y = Math.floor((touch.pageY - rect.top - pixelGap) / (pixelSizeY + pixelGap));
167
+ const x = Math.floor(
168
+ (touch.pageX - rect.left - scalePixelGap) / (scalePixelSizeX + scalePixelGap)
169
+ );
170
+ const y = Math.floor(
171
+ (touch.pageY - rect.top - scalePixelGap) / (scalePixelSizeY + scalePixelGap)
172
+ );
89
173
  const coordinate = `${x},${y}`;
90
174
  if (!this.touchedSquaresSet.has(coordinate)) {
91
175
  this.touchedSquaresSet.add(coordinate);
@@ -94,6 +178,9 @@ export default Render({
94
178
  }
95
179
  },
96
180
  handleTouchend() {
181
+ if (this.isDragging) {
182
+ return;
183
+ }
97
184
  this.isTouchStarted = false;
98
185
  const touchedSquares = [];
99
186
  for (const coordinateStr of this.touchedSquaresSet) {
@@ -143,6 +230,7 @@ export default Render({
143
230
  // 执行填充操作,将圆形内部填充为设定的颜色
144
231
  ctx.fill();
145
232
  }
233
+ this.gridData[`${x}_${y}`] = color;
146
234
  },
147
235
  // 改变画笔颜色
148
236
  updateColor(color) {
@@ -153,13 +241,43 @@ export default Render({
153
241
  this.penColor = color;
154
242
  this.createPixel(color);
155
243
  },
244
+ updateScale(scale) {
245
+ const { canvas } = this;
246
+ if (canvas && scale) {
247
+ canvas.style.transform = `scale(${scale})`;
248
+ this.scale = scale;
249
+ this.scalePixelSizeX = this.pixelSizeX * scale;
250
+ this.scalePixelSizeY = this.pixelSizeY * scale;
251
+ this.scalePixelGap = this.pixelGap * scale;
252
+ }
253
+ },
254
+ setDragging(isDragging) {
255
+ this.isDragging = isDragging;
256
+ const { canvas } = this;
257
+ if (canvas) {
258
+ if (isDragging) {
259
+ canvas.style.touchAction = 'none'; // 禁用默认的触摸行为,防止页面滚动
260
+ } else {
261
+ canvas.style.touchAction = 'auto'; // 恢复默认的触摸行为
262
+ }
263
+ }
264
+ },
156
265
  // 清除画布
157
266
  clear() {
158
267
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
159
268
  this.createPixel(this.pixelColor);
160
269
  },
161
270
  save() {
271
+ const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
272
+ console.log('render save imageData', imageData);
162
273
  const base64 = this.canvas.toDataURL('image/png');
163
- this.callMethod('genImageData', { base64 });
274
+ this.callMethod('genImageData', { base64, gridData: this.gridData });
275
+ },
276
+
277
+ drawData(list) {
278
+ console.log('render drawData', list);
279
+ for (const item of list) {
280
+ this.fillPixel(item.x, item.y, item.color);
281
+ }
164
282
  },
165
283
  });
@@ -1,6 +1,9 @@
1
- <canvas
2
- canvasClassName
3
- type="2d"
4
- canvas-id="{{`${canvasIdPrefix}-sourceCanvas`}}"
5
- style="width: {{realWidth}}px; height: {{realHeight}}px"
6
- />
1
+ <view class="pixelGraffiti_container" id="{{`${canvasIdPrefix}-container`}}" style="width: {{width}}px; height: {{height}}px">
2
+ <view class="smear-wrap">
3
+ <canvas
4
+ type="2d"
5
+ canvas-id="{{`${canvasIdPrefix}-sourceCanvas`}}"
6
+ style="width: {{realWidth}}px; height: {{realHeight}}px"
7
+ />
8
+ </view>
9
+ </view>
package/lib/index.js CHANGED
@@ -27,6 +27,9 @@ const RayGraffiti = props => {
27
27
  needStroke,
28
28
  saveTrigger,
29
29
  clearTrigger,
30
+ scale,
31
+ isDragging,
32
+ drawData,
30
33
  onStrokeChange,
31
34
  onSaveData
32
35
  } = props;
@@ -50,6 +53,9 @@ const RayGraffiti = props => {
50
53
  needStroke: needStroke,
51
54
  saveTrigger: saveTrigger,
52
55
  clearTrigger: clearTrigger,
56
+ scale: scale,
57
+ isDragging: isDragging,
58
+ drawData: drawData,
53
59
  bindstrokeChange: e => onStrokeChange(e.detail),
54
60
  bindsaveData: e => onSaveData(e.detail)
55
61
  }));
package/lib/props.d.ts CHANGED
@@ -102,6 +102,28 @@ export interface IProps {
102
102
  * @default 0
103
103
  */
104
104
  clearTrigger?: number;
105
+ /**
106
+ * @description.zh 画布缩放比例
107
+ * @description.en Canvas scaling ratio
108
+ * @default 1
109
+ */
110
+ scale?: number;
111
+ /**
112
+ * @description.zh 禁止绘制, 画布超出时可拖动画布
113
+ * @description.en Draggable canvas, while disabling brush drawing
114
+ * @default false
115
+ */
116
+ isDragging?: boolean;
117
+ /**
118
+ * @description.zh 初始绘制数据
119
+ * @description.en Initial drawing data
120
+ * @default undefined
121
+ */
122
+ drawData?: Array<{
123
+ x: number;
124
+ y: number;
125
+ color: string;
126
+ }>;
105
127
  /**
106
128
  * @description.zh 画笔数据更新时触发, 返回画笔经过的x,y坐标路径数据
107
129
  * @description.en Triggered when the brush data is updated, returns the x,y coordinate path data of the brush.
@@ -109,8 +131,8 @@ export interface IProps {
109
131
  */
110
132
  onStrokeChange?: (data: IStrokeData) => void;
111
133
  /**
112
- * @description.zh 保存数据时触发, 返回画布的base64数据
113
- * @description.en Triggered when saving data, return canvas base64 data
134
+ * @description.zh 保存数据时触发, 返回画布的 base64 数据和格子颜色数据(gridData, key 为 格子坐标 "x_y",value 为 颜色值)
135
+ * @description.en Triggered when saving data, return canvas base64 data and grid color data
114
136
  * @default () => null
115
137
  */
116
138
  onSaveData?: (data: IData) => void;
@@ -123,5 +145,6 @@ export type IStrokeData = {
123
145
  };
124
146
  export type IData = {
125
147
  base64: string;
148
+ gridData: Record<string, string>;
126
149
  };
127
150
  export declare const defaultProps: IProps;
package/lib/props.js CHANGED
@@ -15,6 +15,8 @@ export const defaultProps = {
15
15
  needStroke: false,
16
16
  saveTrigger: 0,
17
17
  clearTrigger: 0,
18
+ scale: 1,
19
+ isDragging: false,
18
20
  onStrokeChange: () => null,
19
21
  onSaveData: () => null
20
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/graffiti",
3
- "version": "1.0.0",
3
+ "version": "1.1.3",
4
4
  "description": "画布涂鸦组件",
5
5
  "main": "lib/index",
6
6
  "files": [
@@ -31,21 +31,18 @@
31
31
  "prepublishOnly": "yarn build",
32
32
  "release-it": "standard-version"
33
33
  },
34
- "peerDependencies": {
35
- "@ray-js/ray": "^1.4.9"
36
- },
37
- "dependencies": {
38
- "clsx": "^1.2.1"
39
- },
34
+ "dependencies": {},
40
35
  "devDependencies": {
41
36
  "@commitlint/cli": "^7.2.1",
42
37
  "@commitlint/config-conventional": "^9.0.1",
43
- "@ray-js/cli": "^1.4.9",
38
+ "@ray-js/cli": "^1.7.58",
39
+ "@ray-js/code-sandbox": "0.0.7-beta-10",
44
40
  "@ray-js/components-ty-lamp": "^2.0.2",
45
41
  "@ray-js/lamp-saturation-slider": "^1.1.7",
46
42
  "@ray-js/panel-sdk": "^1.13.0-storage.7",
47
- "@ray-js/ray": "^1.4.9",
48
43
  "@vant/stylelint-config": "^1.4.2",
44
+ "@ray-js/ray": "^1.7.58",
45
+ "clsx": "^1.2.1",
49
46
  "core-js": "^3.19.1",
50
47
  "eslint-config-tuya-panel": "^0.4.2",
51
48
  "husky": "^1.2.0",
@@ -53,9 +50,6 @@
53
50
  "standard-version": "9.3.2",
54
51
  "stylelint": "^13.0.0"
55
52
  },
56
- "resolutions": {
57
- "@ray-js/builder-mp": "1.4.15"
58
- },
59
53
  "husky": {
60
54
  "hooks": {
61
55
  "commit-msg": "commitlint -E HUSKY_GIT_PARAMS --config commitlint.config.js",