Skip to content

Commit 7bfd722

Browse files
author
anewzhuang
committed
feat: 修改 renderTiled
1 parent 480c8bb commit 7bfd722

File tree

3 files changed

+136
-75
lines changed

3 files changed

+136
-75
lines changed

docs/components/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ Layout 通过 xml 组织布局,Layout 支持的标签列表如下。
127127

128128
#### 图像渲染模式
129129

130-
> v1.0.29 支持三种图像渲染模式,这些模式既适用于 Image 组件(通过 `imageType` 属性),也适用于所有支持背景图片的容器(通过 `backgroundImageType` 属性)。
130+
> v1.0.16 支持三种图像渲染模式,这些模式既适用于 Image 组件(通过 `imageType` 属性),也适用于所有支持背景图片的容器(通过 `backgroundImageType` 属性)。
131131
132132
| 渲染模式 | 描述 | 适用场景 | 参数要求 | 性能 |
133133
|---------|------|----------|----------|------|

src/common/imageRenderer.ts

Lines changed: 134 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ export class ImageRenderer {
3939
* 简单拉伸渲染
4040
*/
4141
private static renderSimple(
42-
ctx: CanvasRenderingContext2D,
43-
img: HTMLImageElement,
44-
x: number,
45-
y: number,
46-
width: number,
42+
ctx: CanvasRenderingContext2D,
43+
img: HTMLImageElement,
44+
x: number,
45+
y: number,
46+
width: number,
4747
height: number
4848
): void {
4949
ctx.drawImage(img, x, y, width, height);
@@ -53,12 +53,12 @@ export class ImageRenderer {
5353
* 九宫格渲染
5454
*/
5555
private static renderSliced(
56-
ctx: CanvasRenderingContext2D,
57-
img: HTMLImageElement,
58-
x: number,
59-
y: number,
60-
width: number,
61-
height: number,
56+
ctx: CanvasRenderingContext2D,
57+
img: HTMLImageElement,
58+
x: number,
59+
y: number,
60+
width: number,
61+
height: number,
6262
inset?: IInsetParams
6363
): void {
6464
if (!inset) {
@@ -71,139 +71,200 @@ export class ImageRenderer {
7171
const imgWidth = img.width;
7272
const imgHeight = img.height;
7373

74+
// 先检查原始inset值是否合法
75+
if (left < 0 || top < 0 || right < 0 || bottom < 0) {
76+
console.warn('[Layout] inset values cannot be negative, fallback to simple render');
77+
ImageRenderer.renderSimple(ctx, img, x, y, width, height);
78+
return;
79+
}
80+
81+
if (left + right >= imgWidth || top + bottom >= imgHeight) {
82+
console.warn(`[Layout] inset values too large for image size (${imgWidth}x${imgHeight}), fallback to simple render`);
83+
ImageRenderer.renderSimple(ctx, img, x, y, width, height);
84+
return;
85+
}
86+
87+
// 确保inset值不超过图片尺寸(此时已经验证过合法性)
88+
const safeLeft = Math.min(left, imgWidth);
89+
const safeTop = Math.min(top, imgHeight);
90+
const safeRight = Math.min(right, imgWidth);
91+
const safeBottom = Math.min(bottom, imgHeight);
92+
7493
// 计算源区域尺寸
75-
const centerSrcWidth = imgWidth - left - right;
76-
const centerSrcHeight = imgHeight - top - bottom;
94+
const centerSrcWidth = imgWidth - safeLeft - safeRight;
95+
const centerSrcHeight = imgHeight - safeTop - safeBottom;
7796

7897
// 计算目标区域尺寸
79-
const targetCenterWidth = Math.max(0, width - left - right);
80-
const targetCenterHeight = Math.max(0, height - top - bottom);
98+
const targetCenterWidth = Math.max(0, width - safeLeft - safeRight);
99+
const targetCenterHeight = Math.max(0, height - safeTop - safeBottom);
81100

82101
// 1. 渲染四个角(保持原样)
83-
ImageRenderer.renderCorners(ctx, img, x, y, width, height, imgWidth, imgHeight, left, top, right, bottom);
102+
ImageRenderer.renderCorners(ctx, img, x, y, width, height, imgWidth, imgHeight, safeLeft, safeTop, safeRight, safeBottom);
84103

85104
// 2. 渲染四条边(拉伸)
86-
ImageRenderer.renderEdges(ctx, img, x, y, width, height, imgWidth, imgHeight, left, top, right, bottom, centerSrcWidth, centerSrcHeight, targetCenterWidth, targetCenterHeight);
105+
ImageRenderer.renderEdges(ctx, img, x, y, width, height, imgWidth, imgHeight, safeLeft, safeTop, safeRight, safeBottom, centerSrcWidth, centerSrcHeight, targetCenterWidth, targetCenterHeight);
87106

88107
// 3. 渲染中心区域(拉伸)
89108
if (targetCenterWidth > 0 && targetCenterHeight > 0 && centerSrcWidth > 0 && centerSrcHeight > 0) {
90-
ctx.drawImage(img, left, top, centerSrcWidth, centerSrcHeight,
91-
x + left, y + top, targetCenterWidth, targetCenterHeight);
109+
ctx.drawImage(img, safeLeft, safeTop, centerSrcWidth, centerSrcHeight,
110+
x + safeLeft, y + safeTop, targetCenterWidth, targetCenterHeight);
92111
}
93112
}
94113

95114
/**
96115
* 平铺渲染
97116
*/
98117
private static renderTiled(
99-
ctx: CanvasRenderingContext2D,
100-
img: HTMLImageElement,
101-
x: number,
102-
y: number,
103-
width: number,
118+
ctx: CanvasRenderingContext2D,
119+
img: HTMLImageElement,
120+
x: number,
121+
y: number,
122+
width: number,
104123
height: number
105124
): void {
106-
const pattern = ctx.createPattern(img, 'repeat');
107-
if (!pattern) return;
125+
const imgWidth = img.width;
126+
const imgHeight = img.height;
108127

109128
ctx.save();
110-
129+
111130
// 设置裁剪区域
112131
ctx.beginPath();
113132
ctx.rect(x, y, width, height);
114133
ctx.clip();
115-
116-
// 设置pattern并填充
117-
ctx.fillStyle = pattern;
118-
ctx.fillRect(x, y, width, height);
119-
134+
135+
// 预计算完整块和边界块的数量,避免重复计算
136+
const fullCols = Math.floor(width / imgWidth);
137+
const fullRows = Math.floor(height / imgHeight);
138+
const hasPartialCol = width % imgWidth > 0;
139+
const hasPartialRow = height % imgHeight > 0;
140+
141+
// 1. 优先绘制所有完整的块(最常见的情况,无需边界计算)
142+
for (let row = 0; row < fullRows; row++) {
143+
const drawY = y + row * imgHeight;
144+
for (let col = 0; col < fullCols; col++) {
145+
const drawX = x + col * imgWidth;
146+
ctx.drawImage(img, drawX, drawY);
147+
}
148+
}
149+
150+
// 2. 处理右边界的部分列(如果存在)
151+
if (hasPartialCol) {
152+
const partialWidth = width - fullCols * imgWidth;
153+
const partialColX = x + fullCols * imgWidth;
154+
155+
// 完整行的部分列
156+
for (let row = 0; row < fullRows; row++) {
157+
const drawY = y + row * imgHeight;
158+
ctx.drawImage(img, 0, 0, partialWidth, imgHeight, partialColX, drawY, partialWidth, imgHeight);
159+
}
160+
161+
// 右下角的部分块(如果底边界也是部分的)
162+
if (hasPartialRow) {
163+
const partialHeight = height - fullRows * imgHeight;
164+
const partialRowY = y + fullRows * imgHeight;
165+
ctx.drawImage(img, 0, 0, partialWidth, partialHeight, partialColX, partialRowY, partialWidth, partialHeight);
166+
}
167+
}
168+
169+
// 3. 处理底边界的部分行(如果存在)
170+
if (hasPartialRow) {
171+
const partialHeight = height - fullRows * imgHeight;
172+
const partialRowY = y + fullRows * imgHeight;
173+
174+
// 完整列的部分行
175+
for (let col = 0; col < fullCols; col++) {
176+
const drawX = x + col * imgWidth;
177+
ctx.drawImage(img, 0, 0, imgWidth, partialHeight, drawX, partialRowY, imgWidth, partialHeight);
178+
}
179+
}
180+
120181
ctx.restore();
121182
}
122183

123184
/**
124185
* 渲染九宫格的四个角
125186
*/
126187
private static renderCorners(
127-
ctx: CanvasRenderingContext2D,
128-
img: HTMLImageElement,
129-
x: number,
130-
y: number,
131-
width: number,
132-
height: number,
133-
imgWidth: number,
134-
imgHeight: number,
135-
left: number,
136-
top: number,
137-
right: number,
188+
ctx: CanvasRenderingContext2D,
189+
img: HTMLImageElement,
190+
x: number,
191+
y: number,
192+
width: number,
193+
height: number,
194+
imgWidth: number,
195+
imgHeight: number,
196+
left: number,
197+
top: number,
198+
right: number,
138199
bottom: number
139200
): void {
140201
// 左上角
141202
if (left > 0 && top > 0) {
142203
ctx.drawImage(img, 0, 0, left, top, x, y, left, top);
143204
}
144-
205+
145206
// 右上角
146207
if (right > 0 && top > 0) {
147-
ctx.drawImage(img, imgWidth - right, 0, right, top,
148-
x + width - right, y, right, top);
208+
ctx.drawImage(img, imgWidth - right, 0, right, top,
209+
x + width - right, y, right, top);
149210
}
150-
211+
151212
// 左下角
152213
if (left > 0 && bottom > 0) {
153-
ctx.drawImage(img, 0, imgHeight - bottom, left, bottom,
154-
x, y + height - bottom, left, bottom);
214+
ctx.drawImage(img, 0, imgHeight - bottom, left, bottom,
215+
x, y + height - bottom, left, bottom);
155216
}
156-
217+
157218
// 右下角
158219
if (right > 0 && bottom > 0) {
159220
ctx.drawImage(img, imgWidth - right, imgHeight - bottom, right, bottom,
160-
x + width - right, y + height - bottom, right, bottom);
221+
x + width - right, y + height - bottom, right, bottom);
161222
}
162223
}
163224

164225
/**
165226
* 渲染九宫格的四条边
166227
*/
167228
private static renderEdges(
168-
ctx: CanvasRenderingContext2D,
169-
img: HTMLImageElement,
170-
x: number,
171-
y: number,
172-
width: number,
173-
height: number,
174-
imgWidth: number,
175-
imgHeight: number,
176-
left: number,
177-
top: number,
178-
right: number,
179-
bottom: number,
180-
centerSrcWidth: number,
181-
centerSrcHeight: number,
182-
targetCenterWidth: number,
229+
ctx: CanvasRenderingContext2D,
230+
img: HTMLImageElement,
231+
x: number,
232+
y: number,
233+
width: number,
234+
height: number,
235+
imgWidth: number,
236+
imgHeight: number,
237+
left: number,
238+
top: number,
239+
right: number,
240+
bottom: number,
241+
centerSrcWidth: number,
242+
centerSrcHeight: number,
243+
targetCenterWidth: number,
183244
targetCenterHeight: number
184245
): void {
185246
// 上边 - 水平拉伸
186247
if (top > 0 && targetCenterWidth > 0) {
187248
ctx.drawImage(img, left, 0, centerSrcWidth, top,
188-
x + left, y, targetCenterWidth, top);
249+
x + left, y, targetCenterWidth, top);
189250
}
190-
251+
191252
// 下边 - 水平拉伸
192253
if (bottom > 0 && targetCenterWidth > 0) {
193254
ctx.drawImage(img, left, imgHeight - bottom, centerSrcWidth, bottom,
194-
x + left, y + height - bottom, targetCenterWidth, bottom);
255+
x + left, y + height - bottom, targetCenterWidth, bottom);
195256
}
196-
257+
197258
// 左边 - 垂直拉伸
198259
if (left > 0 && targetCenterHeight > 0) {
199260
ctx.drawImage(img, 0, top, left, centerSrcHeight,
200-
x, y + top, left, targetCenterHeight);
261+
x, y + top, left, targetCenterHeight);
201262
}
202-
263+
203264
// 右边 - 垂直拉伸
204265
if (right > 0 && targetCenterHeight > 0) {
205266
ctx.drawImage(img, imgWidth - right, top, right, centerSrcHeight,
206-
x + width - right, y + top, right, targetCenterHeight);
267+
x + width - right, y + top, right, targetCenterHeight);
207268
}
208269
}
209270
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class Layout extends Element {
6666
/**
6767
* 当前 Layout 版本,一般跟小游戏插件版本对齐
6868
*/
69-
public version = '1.0.15';
69+
public version = '1.0.16';
7070

7171
env = env;
7272

0 commit comments

Comments
 (0)