欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > uniapp 给画作生成画框

uniapp 给画作生成画框

2024/10/24 11:20:47 来源:https://blog.csdn.net/weixin_43073383/article/details/141206351  浏览:    关键词:uniapp 给画作生成画框
<template><ax-page class="privateCustom"><gui-page :customHeader="true" ref="guipage"><template #gHeader><aHeader title="个性定制" :showTitle="true" back="2"></aHeader></template><template v-slot:gBody><view class="privateCustom-body"><form @submit="submit"><view class="gui-bg-white gui-dark-bg-level-3 gui-padding-x"><view class="gui-form-item gui-border-b"><text class="gui-form-label">{{ pageIndex == 0 ? '上传图片' : '已选画作' }} |</text><view class="rowContentDiv"><view v-if="pageIndex == 0"><view@tap="uploadImgClick"class="uploadDiv"v-if="!formData.image"><image class="addImg" :src="addPImg" mode="scaleToFill" /><text>图片</text></view><imagev-else:src="formData.image"class="uploadDiv"mode="aspectFit"@tap="uploadImgClick"></image></view><image:src="canvasSize.imgSrc || formData.image"class="posterImg"mode="aspectFit"@tap="previewImage(canvasSize.imgSrc || formData.image)"></image></view></view><!-- <view class="gui-form-item gui-border-b"><text class="gui-form-label">画作名称 |</text><view class="frame-box content"><inputtype="text"class="gui-form-input"v-model="formData.name"name="name"placeholder="请输入内容"/></view></view> --><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">画框选择 |</text><view class="frame-box content"><viewstyle="display: flex; align-items: center"v-for="(item, index) in selectFrame":key="index":class="['frame-item',{ 'frameImg-active': index == formData.frame }]"@tap="frameClick(index)"><image:src="$App.ossImage(item.url)"class="frameImg"mode="aspectFit"/><text>{{ item.name }}</text></view></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">选择画纸 |</text><view class="frame-box content"><viewstyle="display: flex; align-items: center"v-for="(item, index) in papers":key="index":class="['frame-item',{ 'frameImg-active': index == formData.paper }]"@tap="paperClick(index)"><image:src="$App.ossImage(item.url)"class="frameImg"mode="aspectFit"/><text>{{ item.name }}</text></view></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">画框尺寸 |</text><view class="frame-box content"><picker@change="selectSize":value="currentSize"range-key="key"v-if="selectSizeMenu.length > 0":range="selectSizeMenu"><view class="uni-input">{{selectSizeMenu[currentSize].key}}</view></picker></view></view><viewclass="gui-form-item gui-border-b"v-if="currentSize == selectSizeMenu.length - 1 && pageIndex == 0"><text class="gui-form-label">自定义尺寸</text><view class="frame-box content"><inputtype="number"class="gui-form-input"v-model="formData.size"name="size"placeholder="最小10"@blur="getImageSize":min="1"/></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">卡纸宽度 |</text><view class="frame-box content"><picker@change="selectWidths":value="formData.width"range-key="w"v-if="widths.length > 0":range="widths"><view class="uni-input">{{ widths[formData.width].w }}cm</view></picker></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">选择数量 |</text><view class="frame-box content"><gui-step-box:inputClass="['gui-step-box-input','gui-border-radius','gui-bg-gray','gui-dark-bg-level-2']"@change="stepChange":value="formData.num":minNum="1"></gui-step-box></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 0"><text class="gui-form-label">指导价格 |</text><view class="price-box content"><image :src="pricePImg" class="pricePImg" /><text class="price-text">{{Utils.amountS(formData.price)}}</text></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 1"><text class="gui-form-label">作品备注 |</text><view class="frame-box content"><inputtype="text"class="gui-form-input"v-model="formData.mark"name="mark"placeholder="请输入备注"/></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 1"><text class="gui-form-label">收货地址 |</text><view class="frame-box content" @tap="goAddressList"><viewclass="left-part"v-if="Object.keys(formData.address).length > 0"><view class="name">{{ formData.address.recipient }}{{ formData.address.phone }}</view><view class="address-detail">{{`${formData.address.address}${formData.address.detail}`}}</view></view><view class="left-part" v-else><view class="name"> 请选择收货地址 </view></view><!-- <view class="right-part"><textclass="gui-list-arrow-right gui-icons gui-color-gray-light">&#xe601;</text></view> --></view></view><view class="gui-form-item gui-border-b" v-if="pageIndex == 1"><text class="gui-form-label">提示 |</text><view class="frame-box content" style="color: red">定制商品,非商品自身问题,暂不支持退换服务,请您在确认订单前仔细核对商品信息,如有任何疑问,请及时联系我们。感谢您的理解与支持!</view></view></view><buttonclass="gui-button gui-bg-primary gui-noborder submitBtn"formType="submit"><text class="gui-color-white gui-button-text">{{pageIndex == 0 ? '去下单' : '确认下单'}}</text></button><view style="height: 60rpx"></view></form><view class="canvas-in"><canvasv-if="canvasSize.heightIn > 0":style="{width: canvasSize.widthIn + 'px',height: canvasSize.heightIn + 'px',opacity: 0}"canvas-id="graceCanvas"class="grace-canvas"></canvas></view></view></template></gui-page></ax-page>
</template>
<script setup lang="ts">
import App from '@/script/module/App'
import PublicImg from '@/components/publicImage.vue'
import User from '@/script/module/User'
import { axj } from '@/script/sdk/ms-store'
import axConfig from '@/script/AxConfig'
import {onBackPress,onLoad,onPageScroll,onReachBottom,onShow,onUnload
} from '@dcloudio/uni-app'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import Page from '@/script/module/Page'
import Utils from '@/script/util/Utils'
import Artist from '@/script/module/Artist'
import addPImg from '../images/addP.png'
import pricePImg from '../images/priceP.png'
import SdkActs from '@/script/module/SdkActs'const pageIndex = ref(0)
const selectSizeMenu = ref([])
const sizeList = ref([])
const selectFrame = ref([])
const papers = ref([])
const widths = ref([])const formData = reactive({name: '',mark: '',image: '',frame: 0,num: 1,price: <number | string>'',address: User.state.address,size: <any>10,width: 0,paper: 0
})
const currentSize = ref(0)
const customOrderCfg = ref(<axj.DtDCustomOrderCfg>undefined)
const imgInfo = ref({width: 0,height: 0,aspectRatio: 0
})const canvasSize = reactive({widthIn: 0, // 自动计算转换为 pxheightIn: 0, //  自动计算转换为 pxbgColor: '#ffffff', // 背景颜色bgImg: '',contentImg: '',imgSrc: '',multiple: 1, // 将画布放大 2.0 - 2.9 倍(支持小数,过大app端会出现无法渲染的问题),保存的图片更清晰bw: 0,iw: 0,ih: 0,w: 0,h: 0
})
const context = ref(<any>null)const imagesPath = ref(<any>[])// 选择图片
const uploadImgClick = () => {uni.chooseImage({count: 1,extension: ['jpeg', 'jpg', 'png'],success: (res) => {console.log('[  getImageInfo  ] >', res)formData.image = res.tempFilePaths[0]getImageInfo(res.tempFilePaths[0])formData.name = res?.tempFiles[0]?.name || ''}})
}// 获取图片信息
const getImageInfo = (tempFilePath) => {uni.getImageInfo({src: tempFilePath,success: (info) => {const width = info.widthconst height = info.height// 获取宽高比,用于自定义尺寸时候计算宽度const aspectRatio = width / heightimgInfo.value = {width: width,height: height,aspectRatio: aspectRatio}initSize()customOrder()}})
}// 自定义尺寸重绘画布
const getImageSize = () => {customOrder()initSize()
}// 上传图片
const uploadFun = (filePath,name: string = new Date().getTime() + 'image',back: () => any
) => {Artist.fileUpload(filePath, name, 'png', (url) => {uni.hideLoading()formData.image = urlif (back) {back()}})
}
// 选择画框
const frameClick = (index) => {formData.frame = indexinitSize()customOrder()
}const paperClick = (index) => {formData.paper = indexcustomOrder()
}// 定做数量
const stepChange = (e) => {if (e && e[0]) {formData.num = e[0]customOrder()}
}// 选择尺寸
const selectSize = (e) => {// console.log(e)currentSize.value = e.detail.valueif (currentSize.value == selectSizeMenu.value.length - 1) {formData.size = selectSizeMenu.value[0].w} else {formData.size = selectSizeMenu.value[e.detail.value].winitSize()customOrder()}
}// 选择卡纸宽度
const selectWidths = (e) => {formData.width = e.detail.valueinitSize()customOrder()
}// 选择地址
const goAddressList = () => {SdkActs.address(null, null, null)
}// 提交订单
const submit = (e) => {if (formData.image.length == 0) {App.toast('请上传图片')return}if (pageIndex.value == 1) {if (!formData.address) {App.toast('请选择收货地址')return}uni.showModal({title: '提示',content: '确认要提交订单吗?',success: (res) => {if (res.confirm) {// 上传图片后回调提交订单uploadFun(formData.image, formData.name, () => {customOrder(true)})} else if (res.cancel) {// 用户点击取消按钮console.log('用户点击取消')}}})} else {pageIndex.value = 1}
}/*** 自定义画框订单(包括预览逻辑)  confirm 	确认订单(下单)* 流程 : 提交订单-订单详情-支付*/
const customOrder = (confirm?: boolean) => {if (formData.image.length == 0) {App.toast('请上传图片')return}// if (!formData.name) {//   App.toast('请输入画作名称')//   return// }let sizes = selectSizeMenu.value[currentSize.value]let params = {image: formData?.image, //	  画芯// name: formData?.name, //		画作名称frameIdx: formData.frame, //		画框索引paperIdx: formData.paper, //		画纸索引widthIdx: formData.width, //		卡纸宽度索引// attName: selectFrame.value[formData.frame]?.name, //		画框名称w: sizes?.w, //		宽(cm)h: sizes?.h, //		高(cm)num: formData.num, //		数量mark: formData.mark, //		备注confirm: confirm || false, //		确认订单(下单)address: formData.address //		收货地址}App.client.store.Api_order.customOrder(1,[params],(err: any, res: axj.DtDOrderRep) => {console.log('[ customOrder ] >', err, res)formData.price = res.amountif (res?.orderId && confirm) {// uni.redirectTo({//   url: `/pagesShop/shop/cashierDesk?orderId=${res.orderId}`// })Page.navUri(`/pagesShop/order/orderDetail?orderId=${res.orderId}`)} else {// initSize()}})
}
// 获取初始化信息
const getCustomOrderCfg = () => {App.client.store.Api_order.customOrderCfg(1,[],(err: any, res: axj.DtDCustomOrderCfg) => {customOrderCfg.value = resif (res) {if (res.sizes) {// selectSizeMenu.value = ['其他']// 拼接尺寸显示内容for (let i = 0; i < res.sizes.length; i++) {const el = res.sizes[i]if (el.open) {el.key = el.w + 'cm*' + el.h + 'cm'// selectSizeMenu.value.unshift(size)}}selectSizeMenu.value = res.sizes// 增加自定义尺寸选项let obj = {name: '其他',w: res.sizes[0].w || 10,h: res.sizes[0].h || 10,open: true,key: '其他'}formData.size = obj.w//@ts-ignoreselectSizeMenu.value.push(obj)}if (res.frames) {selectFrame.value = res.frames}if (res.papers) {papers.value = res.papers}if (res.widths) {widths.value = res.widths}}})
}// 判断是否存在相同图片,存在则直接显示,不存在则生成
const checkNameExists = (back: (res: any) => any) => {let imgPath = imagesPath.valuelet name =selectSizeMenu.value[currentSize.value].key +'_' +selectSizeMenu.value[currentSize.value].w +'_' +formData.name +selectFrame.value[formData.frame].name +widths.value[formData.width].wconst existingImage = imgPath.find((image) => image.name === name)if (existingImage) {canvasSize.imgSrc = existingImage.urlback(existingImage)} else {back(null)}
}// w h 图片+画框的宽高  ,bw 画框尺寸 ,iw ih 图片宽高
const initSize = () => {if (!formData.image) {return}// 如果是自定义宽度,根据图片宽高比,计算高度if (currentSize.value == selectSizeMenu.value.length - 1) {formData.size = parseInt(formData.size)formData.size = Math.max(formData.size, 10)if (formData.size) {const width = formData.sizeconst height = width / (imgInfo.value.aspectRatio || 1)selectSizeMenu.value[selectSizeMenu.value.length - 1].w = width.toFixed(2)selectSizeMenu.value[selectSizeMenu.value.length - 1].h =height.toFixed(2)}}checkNameExists((res) => {if (!res) {let width = selectSizeMenu.value[currentSize.value].wlet height = selectSizeMenu.value[currentSize.value].hlet imgWidth = imgInfo.value.widthlet imgHeight = imgInfo.value.heightlet cardBoardWidth = widths.value[formData.width].wif (imgWidth >= imgHeight != width >= height) {// 画框方向和画面方向一致let swap = widthwidth = heightheight = swap}let imgW_H = imgWidth / imgHeightif (imgW_H >= width / height) {imgWidth = width - (cardBoardWidth + 2) * 2imgHeight = imgWidth / imgW_H} else {imgHeight = height - (cardBoardWidth + 2) * 2imgWidth = imgHeight * imgW_H}let w = widthlet h = heightlet bw = 2let iw = imgWidthlet ih = imgHeightconsole.log('[  initSize  ] >', w, h, iw, bw, ih)if (w >= h !== iw >= ih) {var t = ww = hh = t}var s = 750 / wif (!s) {return}canvasSize.w = 750canvasSize.h = h * scanvasSize.bw = bw * scanvasSize.iw = iw * scanvasSize.ih = ih * scanvasSize.widthIn = canvasSize.wcanvasSize.heightIn = canvasSize.hsetTimeout(() => {draw()}, 1000)}})
}// 绘制画框图
const draw = () => {uni.showLoading({ title: '正在生成画框图...' })step01()let img = selectFrame.value[formData.frame].urlif (formData.image != '') {drawBGIMG(App.ossImage(img), () => {step03()})} else {step03()}
}const step01 = () => {context.value.setFillStyle(canvasSize.bgColor)context.value.fillRect(0, 0, canvasSize.widthIn, canvasSize.heightIn)
}
// 绘制边框
const drawBGIMG = (img, callback) => {uni.downloadFile({url: img,success: (res) => {if (res.statusCode == 200) {// 绘制uni.getImageInfo({src: res.tempFilePath,success: (res2) => {var pattern = context.value.createPattern(res.tempFilePath,'repeat')context.value.fillStyle = patternvar scale = canvasSize.bw / res2.heightvar w_2 = canvasSize.w * 0.5var h_2 = canvasSize.h * 0.5// 顶部边框context.value.save()context.value.rotate(0)context.value.scale(scale, scale)context.value.fillRect(0,0,canvasSize.w / scale,canvasSize.bw / scale)context.value.restore()// 底部边框context.value.save()context.value.translate(w_2, h_2)context.value.rotate(Math.PI)context.value.translate(-w_2, -h_2)context.value.scale(scale, scale)context.value.fillRect(0,0,canvasSize.w / scale,canvasSize.bw / scale)context.value.restore()// 右侧边框context.value.save()context.value.translate(w_2, h_2)context.value.rotate(Math.PI * 0.5)context.value.translate(-h_2, -w_2)context.value.scale(scale, scale)// 开始绘制梯形路;context.value.beginPath()context.value.moveTo(0, 0)context.value.lineTo(canvasSize.h / scale, 0)context.value.lineTo(canvasSize.h / scale - canvasSize.bw / scale,canvasSize.bw / scale)context.value.lineTo(canvasSize.bw / scale, canvasSize.bw / scale)context.value.closePath()// 填充梯形区域context.value.fill()context.value.restore()// 左侧边框context.value.save()context.value.translate(w_2, h_2)context.value.rotate(-Math.PI * 0.5)context.value.translate(-h_2, -w_2)context.value.scale(scale, scale)// 开始绘制梯形路;context.value.beginPath()context.value.moveTo(0, 0)context.value.lineTo(canvasSize.h / scale, 0)context.value.lineTo(canvasSize.h / scale - canvasSize.bw / scale,canvasSize.bw / scale)context.value.lineTo(canvasSize.bw / scale, canvasSize.bw / scale)context.value.closePath()// 填充梯形区域context.value.fill()context.value.restore()callback()}})}},fail: function (e) {uni.hideLoading()console.log(e)}})
}
// 绘制图片
const step03 = () => {uni.downloadFile({url: formData.image.startsWith('tmp/')? App.ossImage(formData.image): formData.image,success: (res) => {if (res.statusCode === 200) {context.value.drawImage(res.tempFilePath,(canvasSize.w - canvasSize.iw) / 2,(canvasSize.h - canvasSize.ih) / 2,canvasSize.iw,canvasSize.ih)// 在最后一步执行 drawIt 完整最终的绘制drawIt()}},fail: function (e) {console.log(e)}})
}const drawIt = () => {context.value.draw(true, () => {setTimeout(() => {uni.canvasToTempFilePath({x: 0,y: 0,width: canvasSize.widthIn,height: canvasSize.heightIn,destWidth: canvasSize.widthIn,destHeight: canvasSize.heightIn,canvasId: 'graceCanvas',success: (res) => {// 在H5平台下,tempFilePath 为 base64canvasSize.imgSrc = res.tempFilePathlet name =selectSizeMenu.value[currentSize.value].key +'_' +selectSizeMenu.value[currentSize.value].w +'_' +formData.name +selectFrame.value[formData.frame].name +widths.value[formData.width].wcheckNameExists((res) => {if (!res) {imagesPath.value.push({name: name,url: canvasSize.imgSrc})}})uni.hideLoading()}})}, 1000)})
}
const previewImage = (url) => {;(url = url.startsWith('tmp/') ? App.ossImage(url) : url),uni.previewImage({urls: [url],current: 0})
}const kf = () => {Page.openHelp(JSON.stringify(data))
}onShow(() => {// 必须放在show防止登录后返回不触发getCustomOrderCfg()
})onLoad((option) => {if (!User.state?.logined) {Page.navLogin()}// 监听选择地址返回数据uni.$on('addressSelectBack', (res) => {formData.address = res})context.value = uni.createCanvasContext('graceCanvas')
})const goBack = () => {if (pageIndex.value == 1) {pageIndex.value = 0} else {// 物理返回nextTick(() => {Page.navBack()})}
}
onBackPress((e: any) => {if (e && e.from === 'backbutton') {goBack()return true}
})
onUnload(() => {uni.$off('addressSelectBack')
})
</script>
<style scoped lang="scss">
.privateCustom {background: #fbfbfb;position: relative;.privateCustom-body {position: relative;//   margin-top: 38rpx;}// .prvate-line {//   width: 2rpx;//   height: 20rpx;//   background: #000;//   margin-right: 44rpx;//   margin-left: 15rpx;//   margin-top: 6rpx;// }.content {width: 500rpx;}.rowContentDiv {display: flex;justify-content: space-around;width: 500rpx;}.posterImg {height: 200rpx;width: 200rpx;}.uploadDiv {height: 200rpx;width: 200rpx;display: flex;justify-content: center;align-items: center;flex-direction: column;font-weight: 400;font-size: 26rpx;color: #a1a1a1;border: 2rpx dashed black;.addImg {width: 46rpx;height: 46rpx;flex: none;}}:deep(.gui-form-label) {font-weight: 400;font-size: 24rpx;color: #323232;margin-right: 20rpx !important;// line-height: normal !important;}:deep(.gui-form-item) {// align-items: flex-start !important;height: auto !important;// line-height: normal !important;width: auto !important;// margin-top: 80rpx;// margin-top: 55rpx;margin-top: 15rpx;}.frame-box {flex-wrap: wrap;display: flex;.frame-item {border: 2rpx solid #fff;padding: 4rpx;display: flex;flex-direction: column;justify-content: center;align-items: center;margin-right: 4px;}.frameImg {width: 50rpx;height: 50rpx;margin-bottom: 18rpx;}// .frameImg:nth-child(2n - 1) {//   margin-right: 20rpx;// }.frameImg-active {border-color: #961912 !important;}}.price-box {display: flex;font-weight: 400;font-size: 37rpx;color: #ff0000;line-height: 37rpx;height: 48rpx;align-items: center;.pricePImg {width: 48rpx;height: 48rpx;}text {margin-left: 9rpx;}}
}.submitBtn {width: 702rpx;height: 90rpx;background-color: #e4a428 !important;font-weight: 400;font-size: 52rpx;color: #ffffff;line-height: 90rpx;margin-left: 24rpx !important;margin-top: 40rpx !important;
}
.canvas-in {width: 750rpx;overflow: hidden;position: absolute;z-index: 1;left: 0;top: -5000px;
}
</style>

customOrderCfg数据格式

[1,{"frames": [{"price": 1000,"name": "画框一","bName": "这两个是红色的画框","url": "ms-admin/2024/08/15/00i7ihmuzlxdh0mh.png","open": true,"bname": "这两个是红色的画框"},{"price": 1000,"name": "画框二","url": "ms-admin/2024/08/15/00u8ihmuzlrf8nzr.png","open": true},{"price": 1000,"name": "画框三","url": "ms-admin/2024/08/15/00x9ihmuzlj3ey2q.png","open": true}],"papers": [{"price": 10000,"name": "宣纸","url": "ms-admin/2024/08/15/00uxedxuzlba8ebp.png","open": true},{"price": 20000,"name": "油画纸","url": "ms-admin/2024/08/15/0040fdxuzlcxwifk.png","open": true}],"sizes": [{"name": "尺寸一","w": 43.0,"h": 77.0,"open": true},{"name": "尺寸二","w": 50.0,"h": 50.0,"open": true},{"name": "尺寸三","w": 36.0,"h": 96.0,"open": true},{"name": "尺寸四","w": 77.0,"h": 43.0,"open": true}],"widths": [{"name": "大框大画","w": 1.0,"open": true},{"name": "大框中画","w": 10.0,"open": true},{"name": "大框小画","w": 15.0,"open": true}]}
]

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com