1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
import * as THREE from 'three';
/**
* 预加载图像并计算其尺寸
*/
async function preloadImage(imageUrl, axis, canvasHeight, canvasWidth) {
const img = new Image();
img.crossOrigin = 'anonymous';
await new Promise((resolve, reject) => {
img.onload = () => resolve();
img.onerror = () => reject(new Error(`Failed to load image: ${imageUrl}`));
img.src = imageUrl;
});
const aspectRatio = img.naturalWidth / img.naturalHeight;
let calculatedWidth;
let calculatedHeight;
if (axis === 'x') {
// 水平布局:缩放以适应canvasHeight
calculatedHeight = canvasHeight;
calculatedWidth = canvasHeight * aspectRatio;
} else {
// 垂直布局:缩放以适应canvasWidth
calculatedWidth = canvasWidth;
calculatedHeight = canvasWidth / aspectRatio;
}
return { img, width: calculatedWidth, height: calculatedHeight };
}
function calculateCanvasDimensions(imageData, axis, gap, canvasHeight, canvasWidth) {
if (axis === 'x') {
const totalWidth = imageData.reduce(
(sum, data, index) => sum + data.width + (index > 0 ? gap : 0), 0);
return { totalWidth, totalHeight: canvasHeight };
} else {
const totalHeight = imageData.reduce(
(sum, data, index) => sum + data.height + (index > 0 ? gap : 0), 0);
return { totalWidth: canvasWidth, totalHeight };
}
}
function setupCanvas(canvasElement, context, dimensions) {
const { totalWidth, totalHeight } = dimensions;
const devicePixelRatio = Math.min(window.devicePixelRatio || 1, 2);
canvasElement.width = totalWidth * devicePixelRatio;
canvasElement.height = totalHeight * devicePixelRatio;
if (devicePixelRatio !== 1) context.scale(devicePixelRatio, devicePixelRatio);
context.fillStyle = '#ffffff';
context.fillRect(0, 0, totalWidth, totalHeight);
}
function drawImages(context, imageData, axis, gap) {
let currentX = 0;
let currentY = 0;
context.save();
for (const data of imageData) {
context.drawImage(data.img, currentX, currentY, data.width, data.height);
if (axis === 'x') currentX += data.width + gap;
else currentY += data.height + gap;
}
context.restore();
}
function createTextureResult(canvasElement, dimensions) {
const texture = new THREE.CanvasTexture(canvasElement);
texture.needsUpdate = true;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.generateMipmaps = false;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
return {
texture,
dimensions: {
width: dimensions.totalWidth,
height: dimensions.totalHeight,
aspectRatio: dimensions.totalWidth / dimensions.totalHeight,
},
};
}
export async function getCanvasTexture({
images,
gap = 10,
canvasHeight = 512,
canvasWidth = 512,
canvas,
ctx,
axis = 'x',
}) {
if (!images.length) throw new Error('No images');
// 如果未提供,则创建画布和上下文
const canvasElement = canvas || document.createElement('canvas');
const context = ctx || canvasElement.getContext('2d');
if (!context) throw new Error('No context');
// 并行预加载所有图像
const imageData = await Promise.all(
images.map((image) => preloadImage(image.url, axis, canvasHeight, canvasWidth))
);
// 计算总画布尺寸
const dimensions = calculateCanvasDimensions(imageData, axis, gap, canvasHeight, canvasWidth);
// 设置画布
setupCanvas(canvasElement, context, dimensions);
// 绘制所有图像
drawImages(context, imageData, axis, gap);
// 创建并返回纹理结果
return createTextureResult(canvasElement, dimensions)
}
|