// index.js
// 获取应用实例
import { VueLikePage } from "../../utils/page";
import { CDN_URL, API_BASE_URL, VIDEO_BASE_URL, app } from "../../config/index";
VueLikePage([], {
data: {
cdn_url: "",
baseUrl: API_BASE_URL + "/",
url_link: "",
id: "1",
type: "",
loadCompele: false,
filePath: "",
projectid: '',
isEditing: false,
info: {
resourceImg: {},
banner: {},
sceneTitleImg: {},
recordTitleImg: {},
rescan: {},
activeSceneBdImg: {},
},
isZoom: false,
ipsImgList: [],
selectedIp: null,
selectedIpIndex: -1,
ipScaleX: 1,
ipScaleY: 1,
ipRotate: 0,
ipConfirmed: false,
ipLeft: 0,
ipTop: 0,
positionInitialized: false,
canvasWidth: 375,
canvasHeight: 600,
widgetVisible: true,
zoomScrollLeft: 0,
scaleOrientation: '',
baseUniformScale: 1,
confirmedIps: []
},
methods: {
zoom() {
this.setData({
isZoom: !this.data.isZoom,
selectedIp: null,
selectedIpIndex: -1,
ipScaleX: 1,
ipScaleY: 1,
ipRotate: 0,
ipConfirmed: false,
confirmedIps: [],
zoomScrollLeft: 0
});
},
onLoad: function (options) {
let { rdw, id, type, projectid } = options;
if(!projectid){
projectid = 'ZHS2409020-1';
}
this.initIpsList(projectid);
this.getData(projectid);
let link = "";
if (type == "0") {
link = `${VIDEO_BASE_URL}4dvedio/${rdw}.mp4`;
} else {
link = `${VIDEO_BASE_URL}4dpic/${rdw}.jpg`;
// link='https://4dkk.4dage.com/fusion/test/file/797098a37e0c4588ae88f2ec4b1f12df.png'
}
this.setData({
url_link: link,
id,
type,
projectid,
cdn_url: CDN_URL + "/" + projectid,
});
this.downloadF((data) => {
this.setData({
filePath: data,
});
});
},
initIpsList(projectid) {
const list = [];
for (let i = 1; i <= 34; i++) {
list.push({
name: String(i),
imgUrl: `${CDN_URL}/${projectid}/ip/${i}.png`,
});
}
this.setData({
ipsImgList: list,
});
},
loadcompele() {
this.setData({
loadCompele: true,
});
},
cancel() {
// wx.reLaunch({
// url: "/pages/work/index",
// });
wx.navigateBack();
},
edit() {
this.setData({
isEditing: !this.data.isEditing,
});
},
downloadF(cb = () => {}) {
let link = this.data.url_link,
m_type = "";
if (this.data.type == "1") {
m_type = "jpeg";
} else {
m_type = "video";
}
wx.downloadFile({
url: link,
success: (res) => {
if (res.statusCode == "404") {
return app.showAlert("作品暂未生成,请稍后再试", () => {
wx.navigateBack();
});
}
//判断是否为数组
let typeType =
Object.prototype.toString.call(res.header["Content-Type"]) ==
"[object String]"
? res.header["Content-Type"]
: res.header["Content-Type"][0];
//判断不是xml文件
if (typeType.indexOf(m_type) > -1) {
cb(res.tempFilePath);
}
},
fail: () => {
app.showAlert("作品暂未生成,请稍后再试");
},
});
this.setData({
showModal: false,
});
},
async generateImage() {
wx.showLoading({ title: '生成中...', mask: true });
try {
const query = wx.createSelectorQuery();
query.select('.w_video').boundingClientRect();
if (this.data.selectedIp) {
query.select('.ip-overlay').boundingClientRect();
}
query.selectAll('.confirmed-overlay').boundingClientRect();
const res = await new Promise(resolve => query.exec(resolve));
const container = res[0];
const overlay = this.data.selectedIp ? res[1] : null;
const confirmedRects = this.data.selectedIp ? (res[2] || []) : (res[1] || []);
if (!container) throw new Error('Cannot find container');
let width = container.width;
let height = container.height;
if (this.data.isZoom) {
const imgRes = await new Promise((resolve, reject) => {
wx.getImageInfo({
src: this.data.url_link,
success: resolve,
fail: reject
})
});
width = imgRes.width;
height = imgRes.height;
}
// Limit canvas size to avoid incomplete rendering on some devices
const dpr = wx.getSystemInfoSync().pixelRatio;
const maxCanvasSize = 4096;
let scale = 1;
if (width * dpr > maxCanvasSize || height * dpr > maxCanvasSize) {
scale = Math.min(maxCanvasSize / (width * dpr), maxCanvasSize / (height * dpr));
}
const canvasWidth = width * scale;
const canvasHeight = height * scale;
await this._resetWidget(canvasWidth, canvasHeight);
const widget = this.selectComponent('#widget');
const mainUrl = this.data.url_link;
let wxml = '';
if (this.data.isZoom) {
wxml = `
`;
} else {
const bgUrl = this.data.info.resourceImg.bg ? (this.data.cdn_url + this.data.info.resourceImg.bg) : '';
wxml = `
${(this.data.type != '0') ? `` : ''}
`;
}
const style = {
container: { width: canvasWidth, height: canvasHeight, position: 'relative', overflow: 'hidden' },
bg: { width: canvasWidth, height: canvasHeight, position: 'absolute', left: 0, top: 0 },
main: { width: canvasWidth, height: canvasHeight, position: 'absolute', left: 0, top: 0 }
};
await widget.renderToCanvas({ wxml, style });
const ctx = widget.ctx;
const use2d = widget.data.use2dCanvas;
const ratio = canvasWidth / container.width;
// Draw confirmed overlays
for (let i = 0; i < this.data.confirmedIps.length; i++) {
const ov = this.data.confirmedIps[i];
const rect = confirmedRects[i];
if (!ov || !rect) continue;
const stickerInfo = await new Promise((resolve, reject) => {
wx.getImageInfo({
src: ov.imgUrl,
success: resolve,
fail: reject
})
});
let imgToDraw = stickerInfo.path;
if (use2d) {
const canvas = widget.canvas;
const img = canvas.createImage();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = stickerInfo.path;
});
imgToDraw = img;
}
const cx = ((rect.left - container.left + rect.width / 2) + (this.data.isZoom ? this.data.zoomScrollLeft : 0)) * ratio;
const cy = (rect.top - container.top + rect.height / 2) * ratio;
const overlayWidth = rect.width * ratio;
const overlayHeight = rect.height * ratio;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(ov.rotate * Math.PI / 180);
ctx.scale(ov.scaleX, ov.scaleY);
ctx.drawImage(imgToDraw, -overlayWidth / 2, -overlayHeight / 2, overlayWidth, overlayHeight);
ctx.restore();
if (!use2d) {
await new Promise(resolve => ctx.draw(true, resolve));
}
}
// Draw active overlay if exists
if (this.data.selectedIp && overlay) {
const stickerUrl = this.data.selectedIp.imgUrl;
const stickerInfo = await new Promise((resolve, reject) => {
wx.getImageInfo({
src: stickerUrl,
success: resolve,
fail: reject
})
});
let imgToDraw = stickerInfo.path;
if (use2d) {
const canvas = widget.canvas;
const img = canvas.createImage();
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = stickerInfo.path;
});
imgToDraw = img;
}
const cx = ((overlay.left - container.left + overlay.width / 2) + (this.data.isZoom ? this.data.zoomScrollLeft : 0)) * ratio;
const cy = (overlay.top - container.top + overlay.height / 2) * ratio;
const overlayWidth = overlay.width * ratio;
const overlayHeight = overlay.height * ratio;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(this.data.ipRotate * Math.PI / 180);
ctx.scale(this.data.ipScaleX, this.data.ipScaleY);
ctx.drawImage(imgToDraw, -overlayWidth / 2, -overlayHeight / 2, overlayWidth, overlayHeight);
ctx.restore();
if (!use2d) {
await new Promise(resolve => ctx.draw(true, resolve));
}
}
const { tempFilePath } = await widget.canvasToTempFilePath();
// Save
wx.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: () => {
wx.showModal({
title: "提示",
content: "已保存到相册,快去分享吧",
showCancel: false,
});
},
fail: (e) => {
if (!(e.errMsg.indexOf("cancel") > -1)) {
wx.showModal({
title: "提示",
content: "保存失败,请检查是否开启相册保存权限",
showCancel: false,
});
}
}
});
} catch (e) {
console.error(e);
wx.showToast({ title: '生成失败', icon: 'none' });
} finally {
wx.hideLoading();
}
},
async _resetWidget(width, height) {
this.setData({ widgetVisible: false });
await new Promise(r => setTimeout(r, 50));
this.setData({ canvasWidth: width, canvasHeight: height, widgetVisible: true });
await new Promise(r => setTimeout(r, 120));
},
onZoomScroll(e) {
this.setData({ zoomScrollLeft: e.detail.scrollLeft || 0 });
},
saveAlbum() {
let type = this.data.type;
if (this.data.projectid == 'ZHS2409020-1' && type !== '0') {
if (this.data.selectedIp && !this.data.ipConfirmed) {
wx.showToast({
title: '请先确认标签',
icon: 'none'
})
return;
}
this.generateImage();
return;
}
wx.showLoading({
title: "保存中…",
mask: true,
});
if (this.data.filePath) {
let api =
type == "0" ? "saveVideoToPhotosAlbum" : "saveImageToPhotosAlbum";
wx[api]({
filePath: this.data.filePath,
success() {
wx.showModal({
title: "提示",
content: "已保存到相册,快去分享吧",
showCancel: false,
});
},
fail: (e) => {
if (!(e.errMsg.indexOf("cancel") > -1)) {
wx.showModal({
title: "提示",
content:
"保存失败,请检查是否开启相册保存权限,可在「右上角」 - 「设置」里查看",
showCancel: false,
});
}
},
complete: () => {
wx.hideLoading();
},
});
}
},
// 横琴是ZHS2409020-1,替换 ZHS2305758-1
getData(prjId = "ZHS2305758-1") {
wx.showLoading({
title: "资源加载中",
});
this.setData({
cdn_url: CDN_URL + "/" + prjId,
});
wx.request({
url: `${VIDEO_BASE_URL}project/4dage-sxb/${prjId}/config.json`,
success: ({ data: { title, ...rest } }) => {
this.setData(
{
info: rest,
},
() => {
wx.hideLoading();
}
);
wx.setNavigationBarTitle({
title: title,
});
},
});
},
selectIp(e) {
const index = e.currentTarget.dataset.index;
const item = this.data.ipsImgList[index];
if (!item) return;
if (this.data.selectedIp && !this.data.ipConfirmed) {
this.setData({
selectedIpIndex: index,
selectedIp: item
});
} else {
this.setData({
selectedIpIndex: index,
selectedIp: item,
ipScaleX: 1,
ipScaleY: 1,
ipRotate: 0,
ipConfirmed: false,
positionInitialized: false,
}, () => {
this.getOverlayRect();
const query = wx.createSelectorQuery();
query.select('.w_video').boundingClientRect();
query.select('.ip-overlay').boundingClientRect();
query.exec((res) => {
const container = res[0];
const overlay = res[1];
if (container && overlay) {
this.setData({
ipLeft: overlay.left - container.left,
ipTop: overlay.top - container.top,
positionInitialized: true
});
}
});
});
}
},
dragStart(e) {
if (this.data.ipConfirmed) return;
const touch = e.touches[0];
this.setData({
dragStartX: touch.clientX,
dragStartY: touch.clientY,
startIpLeft: this.data.ipLeft,
startIpTop: this.data.ipTop
});
},
dragMove(e) {
if (this.data.ipConfirmed) return;
const touch = e.touches[0];
const dx = touch.clientX - this.data.dragStartX;
const dy = touch.clientY - this.data.dragStartY;
this.setData({
ipLeft: this.data.startIpLeft + dx,
ipTop: this.data.startIpTop + dy
});
},
getOverlayRect() {
const query = wx.createSelectorQuery();
query.select('.ip-overlay').boundingClientRect(rect => {
if (rect) {
this.setData({
centerX: rect.left + rect.width / 2,
centerY: rect.top + rect.height / 2
});
}
}).exec();
},
rotateStart(e) {
this.getOverlayRect();
const touch = e.touches[0];
const dx = touch.clientX - this.data.centerX;
const dy = touch.clientY - this.data.centerY;
const startAngle = Math.atan2(dy, dx) * 180 / Math.PI;
this.setData({
startRotateAngle: startAngle,
baseIpRotate: this.data.ipRotate
});
},
rotateMove(e) {
const touch = e.touches[0];
const dx = touch.clientX - this.data.centerX;
const dy = touch.clientY - this.data.centerY;
const currentAngle = Math.atan2(dy, dx) * 180 / Math.PI;
const diff = currentAngle - this.data.startRotateAngle;
let nextRotate = this.data.baseIpRotate + diff;
this.setData({
ipRotate: nextRotate
});
},
scaleStart(e) {
const touch = e.touches[0];
this.setData({
startX: touch.clientX,
startY: touch.clientY,
baseUniformScale: (this.data.ipScaleX + this.data.ipScaleY) / 2
});
},
scaleMove(e) {
const touch = e.touches[0];
const dy = touch.clientY - this.data.startY;
const factor = 0.005;
let nextScale = this.data.baseUniformScale + (-dy) * factor;
if (nextScale < 0.2) nextScale = 0.2;
if (nextScale > 4) nextScale = 4;
this.setData({ ipScaleX: nextScale, ipScaleY: nextScale });
},
rotateIp() {
// 兼容旧的点击事件,如果不需要可以删除,但保留也不会出错
if (!this.data.selectedIp) return;
const nextRotate = (this.data.ipRotate + 15) % 360;
this.setData({
ipRotate: nextRotate,
});
},
scaleIp() {
if (!this.data.selectedIp) return;
let nextScaleX = this.data.ipScaleX + 0.25;
let nextScaleY = this.data.ipScaleY + 0.25;
if (nextScaleX > 2.5 || nextScaleY > 2.5) {
nextScaleX = 1;
nextScaleY = 1;
}
this.setData({
ipScaleX: nextScaleX,
ipScaleY: nextScaleY,
});
},
deleteIp() {
this.setData({
selectedIp: null,
selectedIpIndex: -1,
ipScaleX: 1,
ipScaleY: 1,
ipRotate: 0,
ipConfirmed: false,
});
},
confirmIp() {
if (!this.data.selectedIp) return;
const overlayItem = {
id: Date.now(),
imgUrl: this.data.selectedIp.imgUrl,
scaleX: this.data.ipScaleX,
scaleY: this.data.ipScaleY,
rotate: this.data.ipRotate,
left: this.data.ipLeft,
top: this.data.ipTop
};
this.setData({
confirmedIps: [...this.data.confirmedIps, overlayItem],
selectedIp: null,
selectedIpIndex: -1,
ipScaleX: 1,
ipScaleY: 1,
ipRotate: 0,
ipConfirmed: false,
positionInitialized: false
});
},
},
});