|
|
@@ -1,7 +1,14 @@
|
|
|
// index.js
|
|
|
// 获取应用实例
|
|
|
-import { VueLikePage } from "../../utils/page";
|
|
|
-import { CDN_URL, API_BASE_URL, VIDEO_BASE_URL, app } from "../../config/index";
|
|
|
+import {
|
|
|
+ VueLikePage
|
|
|
+} from "../../utils/page";
|
|
|
+import {
|
|
|
+ CDN_URL,
|
|
|
+ API_BASE_URL,
|
|
|
+ VIDEO_BASE_URL,
|
|
|
+ app
|
|
|
+} from "../../config/index";
|
|
|
|
|
|
VueLikePage([], {
|
|
|
data: {
|
|
|
@@ -39,9 +46,156 @@ VueLikePage([], {
|
|
|
zoomScrollLeft: 0,
|
|
|
scaleOrientation: '',
|
|
|
baseUniformScale: 1,
|
|
|
- confirmedIps: []
|
|
|
+ confirmedIps: [],
|
|
|
+ // 1:贴纸,2:标题,3:日期
|
|
|
+ tabIndex: 1,
|
|
|
+ rgb: 'rgba(13, 121, 217, 1)', //初始值
|
|
|
+ rgbIndex: 1, //0,1,2,3
|
|
|
+ pick: false,
|
|
|
+ titleDatas: [],
|
|
|
+ title: '',
|
|
|
+ //日期
|
|
|
+ pickerValue: [0, 0, 0], // 年、月、日的选中索引
|
|
|
+ years: [], // 年份数组
|
|
|
+ months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 月份数组
|
|
|
+ days: [], // 日期数组(根据年月动态生成)
|
|
|
+ selectedDate: '' // 最终选中的日期
|
|
|
},
|
|
|
methods: {
|
|
|
+ loadDate() {
|
|
|
+ const now = new Date();
|
|
|
+ const currentYear = now.getFullYear();
|
|
|
+ const years = [];
|
|
|
+ for (let i = currentYear - 10; i <= currentYear + 10; i++) {
|
|
|
+ years.push(i);
|
|
|
+ }
|
|
|
+ this.setData({
|
|
|
+ years
|
|
|
+ });
|
|
|
+
|
|
|
+ // 2. 初始化当前日期(默认选中今天)
|
|
|
+ const currentMonth = now.getMonth() + 1; // 月份从0开始,需+1
|
|
|
+ const currentDay = now.getDate();
|
|
|
+
|
|
|
+ // 计算当前年/月/日在数组中的索引
|
|
|
+ const yearIndex = years.indexOf(currentYear);
|
|
|
+ const monthIndex = currentMonth - 1;
|
|
|
+
|
|
|
+ // 初始化日期数组(根据当前年月)
|
|
|
+ this.setDays(currentYear, currentMonth);
|
|
|
+
|
|
|
+ // 设置默认选中值
|
|
|
+ this.setData({
|
|
|
+ pickerValue: [yearIndex, monthIndex, currentDay - 1],
|
|
|
+ selectedDate: `${currentYear}-${this.formatNum(currentMonth)}-${this.formatNum(currentDay)}`
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化数字(补0,比如1→01)
|
|
|
+ formatNum(num) {
|
|
|
+ return num < 10 ? `0${num}` : num;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 根据年、月动态生成日期数组(处理2月、小月/大月)
|
|
|
+ setDays(year, month) {
|
|
|
+ // 计算当月最后一天
|
|
|
+ const lastDay = new Date(year, month, 0).getDate();
|
|
|
+ const days = [];
|
|
|
+ for (let i = 1; i <= lastDay; i++) {
|
|
|
+ days.push(i);
|
|
|
+ }
|
|
|
+ this.setData({
|
|
|
+ days
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 日期选择器滚动变化时触发
|
|
|
+ onDateChange(e) {
|
|
|
+ const [yearIndex, monthIndex, dayIndex] = e.detail.value;
|
|
|
+ const {
|
|
|
+ years,
|
|
|
+ months,
|
|
|
+ days
|
|
|
+ } = this.data;
|
|
|
+
|
|
|
+ // 获取选中的年、月、日
|
|
|
+ const selectedYear = years[yearIndex];
|
|
|
+ const selectedMonth = months[monthIndex];
|
|
|
+ const selectedDay = days[dayIndex];
|
|
|
+
|
|
|
+ // 重新计算日期数组(防止切换年月后,日期超出当月范围,比如31号切到小月)
|
|
|
+ this.setDays(selectedYear, selectedMonth);
|
|
|
+
|
|
|
+ // 更新选中日期和picker值(日期索引可能变化,需重新校准)
|
|
|
+ const newDayIndex = Math.min(dayIndex, this.data.days.length - 1);
|
|
|
+ this.setData({
|
|
|
+ pickerValue: [yearIndex, monthIndex, newDayIndex],
|
|
|
+ selectedDate: `${selectedYear}-${this.formatNum(selectedMonth)}-${this.formatNum(this.data.days[newDayIndex])}`
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 输入时实时更新
|
|
|
+ onTitleInput(e) {
|
|
|
+ this.setData({
|
|
|
+ title: e.detail.value
|
|
|
+ })
|
|
|
+ console.log(123, this.data.title)
|
|
|
+ },
|
|
|
+ saveTitle(e) {
|
|
|
+ const currentTitle = this.data.title.trim()
|
|
|
+
|
|
|
+ // 防空判断
|
|
|
+ if (!currentTitle) {
|
|
|
+ wx.showToast({
|
|
|
+ title: '请输入标题',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 追加
|
|
|
+ this.setData({
|
|
|
+ titleDatas: [...this.data.titleDatas, currentTitle],
|
|
|
+ title: '',
|
|
|
+ })
|
|
|
+
|
|
|
+ this.selectIp(e)
|
|
|
+
|
|
|
+ console.log(this.data.titleDatas)
|
|
|
+ },
|
|
|
+ // 显示取色器
|
|
|
+ toPick: function () {
|
|
|
+ this.setData({
|
|
|
+ pick: !this.data.pick,
|
|
|
+ rgbIndex:5
|
|
|
+ })
|
|
|
+ },
|
|
|
+ //取色结果回调
|
|
|
+ pickColor(e) {
|
|
|
+ let rgb = e.detail.color;
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ rgb: rgb
|
|
|
+ })
|
|
|
+ },
|
|
|
+ //设置颜色
|
|
|
+ setColor(e) {
|
|
|
+ let rgb = e.currentTarget.dataset.rgb;
|
|
|
+ let index = e.currentTarget.dataset.index;
|
|
|
+ console.log(rgb)
|
|
|
+ this.setData({
|
|
|
+ rgb: rgb,
|
|
|
+ rgbIndex: index
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 切换tab
|
|
|
+ handleTabTap(e) {
|
|
|
+ const index = e.currentTarget.dataset.index;
|
|
|
+ console.log(index, '11111')
|
|
|
+ this.setData({
|
|
|
+ tabIndex: index
|
|
|
+ }
|
|
|
+
|
|
|
+ )
|
|
|
+ },
|
|
|
zoom() {
|
|
|
this.setData({
|
|
|
isZoom: !this.data.isZoom,
|
|
|
@@ -56,8 +210,13 @@ VueLikePage([], {
|
|
|
});
|
|
|
},
|
|
|
onLoad: function (options) {
|
|
|
- let { rdw, id, type, projectid } = options;
|
|
|
- if(!projectid){
|
|
|
+ let {
|
|
|
+ rdw,
|
|
|
+ id,
|
|
|
+ type,
|
|
|
+ projectid
|
|
|
+ } = options;
|
|
|
+ if (!projectid) {
|
|
|
projectid = 'ZHS2409020-1';
|
|
|
}
|
|
|
this.initIpsList(projectid);
|
|
|
@@ -68,7 +227,9 @@ VueLikePage([], {
|
|
|
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'
|
|
|
+ // test
|
|
|
+ // link = 'https://pic.616pic.com/phototwo/00/06/02/618e27a7290161785.jpg'
|
|
|
+ this.loadDate()
|
|
|
}
|
|
|
|
|
|
this.setData({
|
|
|
@@ -103,11 +264,20 @@ VueLikePage([], {
|
|
|
});
|
|
|
},
|
|
|
cancel() {
|
|
|
- // wx.reLaunch({
|
|
|
- // url: "/pages/work/index",
|
|
|
- // });
|
|
|
+ if (this.data.isEditing) {
|
|
|
+ this.setData({
|
|
|
+ isEditing: false,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // wx.reLaunch({
|
|
|
+ // url: "/pages/work/index",
|
|
|
+ // });
|
|
|
+
|
|
|
+ wx.navigateBack();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
|
|
|
- wx.navigateBack();
|
|
|
},
|
|
|
edit() {
|
|
|
this.setData({
|
|
|
@@ -115,8 +285,29 @@ VueLikePage([], {
|
|
|
});
|
|
|
},
|
|
|
|
|
|
+ _wrapText(ctx, text, maxWidth) {
|
|
|
+ const chars = text.split('');
|
|
|
+ const lines = [];
|
|
|
+ let currentLine = '';
|
|
|
+
|
|
|
+ for (let char of chars) {
|
|
|
+ const testLine = currentLine + char;
|
|
|
+ if (ctx.measureText(testLine).width <= maxWidth || currentLine === '') {
|
|
|
+ currentLine = testLine;
|
|
|
+ } else {
|
|
|
+ lines.push(currentLine);
|
|
|
+ currentLine = char;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (currentLine) lines.push(currentLine);
|
|
|
+ return lines;
|
|
|
+ },
|
|
|
+
|
|
|
downloadF(cb = () => {}) {
|
|
|
let link = this.data.url_link,
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
m_type = "";
|
|
|
|
|
|
if (this.data.type == "1") {
|
|
|
@@ -124,7 +315,6 @@ VueLikePage([], {
|
|
|
} else {
|
|
|
m_type = "video";
|
|
|
}
|
|
|
-
|
|
|
wx.downloadFile({
|
|
|
url: link,
|
|
|
success: (res) => {
|
|
|
@@ -136,9 +326,9 @@ VueLikePage([], {
|
|
|
//判断是否为数组
|
|
|
let typeType =
|
|
|
Object.prototype.toString.call(res.header["Content-Type"]) ==
|
|
|
- "[object String]"
|
|
|
- ? res.header["Content-Type"]
|
|
|
- : res.header["Content-Type"][0];
|
|
|
+ "[object String]" ?
|
|
|
+ res.header["Content-Type"] :
|
|
|
+ res.header["Content-Type"][0];
|
|
|
|
|
|
//判断不是xml文件
|
|
|
if (typeType.indexOf(m_type) > -1) {
|
|
|
@@ -154,24 +344,41 @@ VueLikePage([], {
|
|
|
});
|
|
|
},
|
|
|
|
|
|
+ drawRoundRect(ctx, x, y, width, height, radius) {
|
|
|
+ ctx.beginPath();
|
|
|
+ // 左上圆角
|
|
|
+ ctx.moveTo(x + radius, y);
|
|
|
+ ctx.arcTo(x + width, y, x + width, y + height, radius);
|
|
|
+ // 右上圆角
|
|
|
+ ctx.arcTo(x + width, y + height, x, y + height, radius);
|
|
|
+ // 右下圆角
|
|
|
+ ctx.arcTo(x, y + height, x, y, radius);
|
|
|
+ // 左下圆角
|
|
|
+ ctx.arcTo(x, y, x + width, y, radius);
|
|
|
+ ctx.closePath(); // 闭合路径
|
|
|
+ },
|
|
|
+
|
|
|
async generateImage() {
|
|
|
- wx.showLoading({ title: '生成中...', mask: true });
|
|
|
+ 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.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;
|
|
|
|
|
|
@@ -189,16 +396,16 @@ VueLikePage([], {
|
|
|
|
|
|
// Limit canvas size to avoid incomplete rendering on some devices
|
|
|
const dpr = wx.getSystemInfoSync().pixelRatio;
|
|
|
- const maxCanvasSize = 4096;
|
|
|
+ const maxCanvasSize = 4096;
|
|
|
let scale = 1;
|
|
|
-
|
|
|
+
|
|
|
if (width * dpr > maxCanvasSize || height * dpr > maxCanvasSize) {
|
|
|
- scale = Math.min(maxCanvasSize / (width * dpr), maxCanvasSize / (height * dpr));
|
|
|
+ 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');
|
|
|
|
|
|
@@ -217,31 +424,163 @@ VueLikePage([], {
|
|
|
<view class="container">
|
|
|
<image class="bg" src="${bgUrl}"></image>
|
|
|
${(this.data.type != '0') ? `<image class="main" src="${mainUrl}"></image>` : ''}
|
|
|
- </view>
|
|
|
+ </view>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
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 }
|
|
|
+ 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;
|
|
|
-
|
|
|
+ await widget.renderToCanvas({
|
|
|
+ wxml,
|
|
|
+ style
|
|
|
+ });
|
|
|
// 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 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;
|
|
|
+ // 贴纸
|
|
|
+ console.log(ov)
|
|
|
+ if (ov.typeIndex == 1) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ 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();
|
|
|
+
|
|
|
+ } else if (ov.typeIndex == 2 || ov.typeIndex == 3) {
|
|
|
+ const text = ov.typeIndex == 2 ? ov.title.trim() : ov.date.trim();
|
|
|
+ if (!text) continue;
|
|
|
+ console.log(text)
|
|
|
+ ctx.save();
|
|
|
+ ctx.translate(cx, cy);
|
|
|
+ ctx.rotate((ov.rotate || 0) * Math.PI / 180);
|
|
|
+ ctx.scale(ov.scaleX || 1, ov.scaleY || 1);
|
|
|
+
|
|
|
+ // ── 参数定义 ──
|
|
|
+ const fontSizeRpx = 40;
|
|
|
+ const fontSizePx = fontSizeRpx * ratio * (container.width / 750);
|
|
|
+
|
|
|
+ const maxWidth = canvasWidth;
|
|
|
+ const minWidth = 80 * ratio;
|
|
|
+ const minHeight = 45 * ratio;
|
|
|
+ const lineHeight = 30;
|
|
|
+ const paddingRpx = 10;
|
|
|
+ const padding = paddingRpx * ratio;
|
|
|
+ const borderRadius = 40 * ratio;
|
|
|
+
|
|
|
+ ctx.font = ` ${fontSizePx}px iconfont`;
|
|
|
+ ctx.fillStyle = ov.rgb || '#000000';
|
|
|
+ ctx.textAlign = 'center';
|
|
|
+ ctx.textBaseline = 'middle';
|
|
|
+
|
|
|
+ // 计算换行
|
|
|
+ let lines = [text];
|
|
|
+ let textWidth = ctx.measureText(text).width;
|
|
|
+ if (textWidth > maxWidth) {
|
|
|
+ lines = this._wrapText(ctx, text, maxWidth);
|
|
|
+ textWidth = Math.max(...lines.map(line => ctx.measureText(line).width));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用 min-width
|
|
|
+ const contentWidth = Math.max(textWidth, minWidth);
|
|
|
+ // 计算总内容高度(文字行高总和)
|
|
|
+ const contentHeight = lines.length * lineHeight;
|
|
|
+ // 最终容器高度(文字高度 + 上下 padding + min-height)
|
|
|
+ const finalHeight = Math.max(contentHeight + padding, minHeight);
|
|
|
+
|
|
|
+ console.log(minHeight)
|
|
|
+
|
|
|
+ // 最终容器宽度(文字宽度 + 左右 padding)
|
|
|
+ const finalWidth = contentWidth + padding * 2;
|
|
|
+
|
|
|
+ // 绘制圆角矩形背景
|
|
|
+ ctx.fillStyle = 'rgba(156, 208, 255, 1)';
|
|
|
+ ctx.beginPath();
|
|
|
+ this.drawRoundRect(
|
|
|
+ ctx,
|
|
|
+ -finalWidth / 2,
|
|
|
+ -finalHeight / 2,
|
|
|
+ finalWidth,
|
|
|
+ finalHeight,
|
|
|
+ 20 // 圆角半径
|
|
|
+ );
|
|
|
+ ctx.fill();
|
|
|
+
|
|
|
+ // ── 绘制文字(在背景之上) ──
|
|
|
+ ctx.fillStyle = ov.rgb || '#000000'; // 恢复文字颜色
|
|
|
+ let yOffset = -contentHeight / 2 + lineHeight / 2; // 文字从容器中间开始
|
|
|
+
|
|
|
+ for (const line of lines) {
|
|
|
+ ctx.fillText(line, 0, yOffset);
|
|
|
+ yOffset += lineHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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: ov.imgUrl,
|
|
|
+ src: stickerUrl,
|
|
|
success: resolve,
|
|
|
fail: reject
|
|
|
})
|
|
|
@@ -259,117 +598,89 @@ VueLikePage([], {
|
|
|
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));
|
|
|
+ await new Promise(resolve => ctx.draw(true, resolve));
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- const { tempFilePath } = await widget.canvasToTempFilePath();
|
|
|
-
|
|
|
+
|
|
|
+ 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,
|
|
|
- });
|
|
|
- }
|
|
|
+ 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' });
|
|
|
+ wx.showToast({
|
|
|
+ title: '生成失败',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
} finally {
|
|
|
wx.hideLoading();
|
|
|
}
|
|
|
},
|
|
|
async _resetWidget(width, height) {
|
|
|
- this.setData({ widgetVisible: false });
|
|
|
+ this.setData({
|
|
|
+ widgetVisible: false
|
|
|
+ });
|
|
|
await new Promise(r => setTimeout(r, 50));
|
|
|
- this.setData({ canvasWidth: width, canvasHeight: height, widgetVisible: true });
|
|
|
+ this.setData({
|
|
|
+ canvasWidth: width,
|
|
|
+ canvasHeight: height,
|
|
|
+ widgetVisible: true
|
|
|
+ });
|
|
|
await new Promise(r => setTimeout(r, 120));
|
|
|
},
|
|
|
onZoomScroll(e) {
|
|
|
- this.setData({ zoomScrollLeft: e.detail.scrollLeft || 0 });
|
|
|
+ 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;
|
|
|
+ if (this.data.selectedIp && !this.data.ipConfirmed) {
|
|
|
+ wx.showToast({
|
|
|
+ title: '请先确认标签',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.generateImage();
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
wx.showLoading({
|
|
|
@@ -393,8 +704,7 @@ VueLikePage([], {
|
|
|
if (!(e.errMsg.indexOf("cancel") > -1)) {
|
|
|
wx.showModal({
|
|
|
title: "提示",
|
|
|
- content:
|
|
|
- "保存失败,请检查是否开启相册保存权限,可在「右上角」 - 「设置」里查看",
|
|
|
+ content: "保存失败,请检查是否开启相册保存权限,可在「右上角」 - 「设置」里查看",
|
|
|
showCancel: false,
|
|
|
});
|
|
|
}
|
|
|
@@ -418,9 +728,13 @@ VueLikePage([], {
|
|
|
|
|
|
wx.request({
|
|
|
url: `${VIDEO_BASE_URL}project/4dage-sxb/${prjId}/config.json`,
|
|
|
- success: ({ data: { title, ...rest } }) => {
|
|
|
- this.setData(
|
|
|
- {
|
|
|
+ success: ({
|
|
|
+ data: {
|
|
|
+ title,
|
|
|
+ ...rest
|
|
|
+ }
|
|
|
+ }) => {
|
|
|
+ this.setData({
|
|
|
info: rest,
|
|
|
},
|
|
|
() => {
|
|
|
@@ -436,6 +750,7 @@ VueLikePage([], {
|
|
|
selectIp(e) {
|
|
|
const index = e.currentTarget.dataset.index;
|
|
|
const item = this.data.ipsImgList[index];
|
|
|
+ // 这里日期和标题选择的index都没用,但是也需要传,相当于限制了添加上限为ips数组长度
|
|
|
if (!item) return;
|
|
|
if (this.data.selectedIp && !this.data.ipConfirmed) {
|
|
|
this.setData({
|
|
|
@@ -443,6 +758,7 @@ VueLikePage([], {
|
|
|
selectedIp: item
|
|
|
});
|
|
|
} else {
|
|
|
+
|
|
|
this.setData({
|
|
|
selectedIpIndex: index,
|
|
|
selectedIp: item,
|
|
|
@@ -453,23 +769,26 @@ VueLikePage([], {
|
|
|
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
|
|
|
- });
|
|
|
- }
|
|
|
+ const container = res[0];
|
|
|
+ const overlay = res[1];
|
|
|
+ console.log(container, overlay, 'index')
|
|
|
+ if (container && overlay) {
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ ipLeft: overlay.left - container.left,
|
|
|
+ ipTop: overlay.top - container.top,
|
|
|
+ positionInitialized: true
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
},
|
|
|
|
|
|
dragStart(e) {
|
|
|
@@ -488,7 +807,7 @@ VueLikePage([], {
|
|
|
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
|
|
|
@@ -513,7 +832,7 @@ VueLikePage([], {
|
|
|
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
|
|
|
@@ -525,10 +844,10 @@ VueLikePage([], {
|
|
|
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
|
|
|
});
|
|
|
@@ -550,9 +869,12 @@ VueLikePage([], {
|
|
|
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 });
|
|
|
+ this.setData({
|
|
|
+ ipScaleX: nextScale,
|
|
|
+ ipScaleY: nextScale
|
|
|
+ });
|
|
|
},
|
|
|
-
|
|
|
+
|
|
|
rotateIp() {
|
|
|
// 兼容旧的点击事件,如果不需要可以删除,但保留也不会出错
|
|
|
if (!this.data.selectedIp) return;
|
|
|
@@ -587,8 +909,12 @@ VueLikePage([], {
|
|
|
confirmIp() {
|
|
|
if (!this.data.selectedIp) return;
|
|
|
const overlayItem = {
|
|
|
+ typeIndex: this.data.tabIndex, // 判断是贴图还是标题还是日期
|
|
|
id: Date.now(),
|
|
|
+ rgb: this.data.rgb,
|
|
|
imgUrl: this.data.selectedIp.imgUrl,
|
|
|
+ title: this.data.titleDatas[this.data.titleDatas.length - 1],
|
|
|
+ date: this.data.selectedDate,
|
|
|
scaleX: this.data.ipScaleX,
|
|
|
scaleY: this.data.ipScaleY,
|
|
|
rotate: this.data.ipRotate,
|
|
|
@@ -607,4 +933,4 @@ VueLikePage([], {
|
|
|
});
|
|
|
},
|
|
|
},
|
|
|
-});
|
|
|
+});
|