prophetk-3d-viewer
Version:
A Three.js r128 based GLB/GLTF model viewer for WeChat Mini Program and Web.
167 lines (147 loc) • 4.65 kB
JavaScript
function appendNoCacheParam(url) {
const sep = url.indexOf('?') === -1 ? '?' : '&';
return url + sep + '_t=' + Date.now();
}
function sanitizeCacheKey(key) {
return key.replace(/[^a-zA-Z0-9_]/g, '_');
}
class WechatPlatformAdapter {
constructor(options = {}) {
this.wx = options.wx || (typeof wx !== 'undefined' ? wx : null);
this.canvas = options.canvas || null;
if (!this.wx) throw new Error('WechatPlatformAdapter requires wx');
}
getWindowInfo() {
return this.wx.getWindowInfo();
}
getPixelRatio() {
const info = this.getWindowInfo();
return info.pixelRatio || 1;
}
getContext(canvas, type) {
return canvas.getContext(type);
}
requestAnimationFrame(canvas, callback) {
return canvas.requestAnimationFrame(callback);
}
cancelAnimationFrame(canvas, id) {
canvas.cancelAnimationFrame(id);
}
createImage(canvas) {
return canvas.createImage();
}
createCanvas(width, height) {
if (!this.wx.createOffscreenCanvas) {
throw new Error('wx.createOffscreenCanvas is required for three.js UI textures');
}
const canvas = this.wx.createOffscreenCanvas({ type: '2d', width, height });
canvas.width = width;
canvas.height = height;
return canvas;
}
requestJson(url) {
return new Promise((resolve, reject) => {
this.wx.request({
url,
dataType: 'json',
header: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
success: (res) => {
try {
resolve(typeof res.data === 'string' ? JSON.parse(res.data) : res.data);
} catch (e) {
reject(e);
}
},
fail: reject,
});
});
}
fetchFile(sourceUrl, options = {}) {
const url = options.url || sourceUrl;
// Packaged paths are relative to the Mini Program root, not this JS file.
// Local files bypass downloading and the remote-file cache.
if (!/^https?:\/\//i.test(url)) {
return Promise.resolve(url.split(/[?#]/)[0].replace(/^\.\//, ''));
}
const shouldCache = options.shouldCache !== false;
const cacheKey = options.cacheKey || sourceUrl;
return new Promise((resolve, reject) => {
const cached = shouldCache ? this._getCachedPath(cacheKey) : '';
if (cached) return resolve(cached);
this.wx.downloadFile({
url: shouldCache ? url : appendNoCacheParam(url),
header: shouldCache ? undefined : {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
success: (res) => {
if (res.statusCode !== 200) {
reject(new Error('Download failed: ' + res.statusCode));
return;
}
if (!shouldCache) {
resolve(res.tempFilePath);
return;
}
const fs = this.wx.getFileSystemManager();
const ext = sourceUrl.split('?')[0].split('.').pop();
const dest = this.wx.env.USER_DATA_PATH + '/' + sanitizeCacheKey(cacheKey) + '.' + ext;
try {
fs.copyFileSync(res.tempFilePath, dest);
this.wx.setStorageSync(cacheKey, dest);
resolve(dest);
} catch (e) {
resolve(res.tempFilePath);
}
},
fail: reject,
});
});
}
fetchArrayBuffer(sourceUrl, options = {}) {
return this.fetchFile(sourceUrl, options).then((filePath) => {
return new Promise((resolve, reject) => {
this.wx.getFileSystemManager().readFile({
filePath,
success: (res) => resolve(res.data),
fail: reject,
});
});
});
}
readFileBase64(filePath) {
return new Promise((resolve, reject) => {
this.wx.getFileSystemManager().readFile({
filePath,
encoding: 'base64',
success: (res) => resolve(res.data),
fail: reject,
});
});
}
readTextFile(filePath) {
return new Promise((resolve, reject) => {
this.wx.getFileSystemManager().readFile({
filePath,
encoding: 'utf8',
success: (res) => resolve(res.data),
fail: reject,
});
});
}
_getCachedPath(cacheKey) {
let cached = '';
try { cached = this.wx.getStorageSync(cacheKey); } catch (e) {}
if (!cached) return '';
try {
this.wx.getFileSystemManager().accessSync(cached);
return cached;
} catch (e) {
return '';
}
}
}
module.exports = WechatPlatformAdapter;