用uniapp跨平台开发一款商用的ai绘图ai作画软件的全过程

随着科技的发展,人工智能将会是以后的主流方向,ai绘图、chatgpt高科技先后出台,都获得了火爆的发展,因此想借着势头开发一款主打ai绘图的app+小程序

一、前期准备

1、选定技术框架

因为后面需要多平台发布,而尽可能减少开发成本,因此选定了uniapp框架作为开发框架,前期目标是 android、ios以及 微信小程序三个平台,android平台名称:易绘,IOS端名称:易绘ai作画,微信小程序端:栾青易绘ai绘画。

2、ai绘图的api选择

有两个方向,一个是百度云,一个是腾讯云,各有优劣,价格方面腾讯云更优惠,但百度推出的时间更早。因为前期接入的是百度云的sdk,所以还是选择了百度云sdk。

3、功能整合

主打ai绘图(用文字生成图片),次功能头像制作、老照片修复处理,真人动漫化头像,二次元图片,壁纸美图。开放了一部分功能免费使用,以留住用户。

二、ai绘图

<template>
	<view>
		<uni-nav-bar :statusBar="true" :border="true" :fixed="true" :leftWidth="500">
			<template v-slot:left class="uni_flex_row_align_left">
				<view class="uni_flex_row_align_left uni_14px uni_font_medium ml14" v-if="isLogined" @click="gotoTaskList">
					<image src="@/static/icon/icon_task.png" style="width: 40rpx;height: 40rpx;margin-left: 10rpx;"></image>
					<text class="uni_15px ml6 uni_color_666">任务列表</text>
					<text class="ml4 mr2">(</text>
					<text class="uni_15px" style="color: #f43533;">{{taskNum}}</text>
					<text class="ml2">)</text>
				</view>
				<view v-else class="uni_14px uni_font_regular ml14 uni_color_666">
					用文字绘出梦想,用梦想成就未来
				</view>
			</template>
		</uni-nav-bar>
	
		<!-- 主绘制面板视图 -->
		<view class="page_root">
			
			<view style="width: 690rpx;height: 15rpx;"></view>
			<!-- 输入框内容 START -->
			<view class="input_box">
				<!-- 文本输入框 -->
				<textarea class="textarea_style uni_font_light" :maxlength="maxNum" placeholder="输入你的奇思妙想,AI智能为你作画" v-model="inputContent"></textarea>
				
				<!-- 信息操作栏 START -->
				<view class="input_action_bar">
					<view class="uni_flex_row_align_left" @click="startVoice">
						<uni-icons type="mic-filled" color="#9686FB" size="19"></uni-icons>
						<text class="uni_14px ml4 uni_font_regular input_func_text" style="color: #9686FB;">语音输入</text>
					</view>
					
					<view class="uni_flex_row_align_right">
						<view class="uni_flex_row_align_left" v-if="inputContent">
							<text class="num g1">{{inputContent.length || 0}}</text>
							<text class="num g2 ml4 mr4">/</text>
							<text class="num g2">{{maxNum}}</text>
						</view>
						
						<view class="uni_flex_row_align_right" v-else @click="gotoSchool">
							<uni-icons type="help" size="21" color="#9686FB"></uni-icons>
							<text class="uni_14px ml4 uni_font_regular input_func_text" style="color: #9686FB;">教程帮助</text>
						</view>
						
						<view class="sp_line" v-if="inputContent"></view>
						
						<!-- 清除 -->
						<uni-icons type="clear" v-if="inputContent" color="#9686FB" size="19" @click="onClear" click="ml10"></uni-icons>

						<!-- 复制 -->
						<uni-icons type="paperclip" v-if="inputContent" color="#9686FB" size="19" class="ml20" @click="onCopy"></uni-icons>
						
					</view>
				</view>
				<!-- 信息操作栏 START -->
			</view>
			<!-- 输入框内容 END -->
			
			<!-- 示例 START -->
			<view class="uni_flex_row_between mt6" v-if="exampleObj.id">
				<!-- 示例内容 -->
				<text class="tip_text" @click="onClickExample">示例:{{exampleObj.content}}</text>
				<!-- 刷新示例数据 -->
				<uni-icons :animation="animationData" type="loop" color="#9686FB" class="refresh_icon" size="18" @click="queryExample"></uni-icons>
			</view>
			<!-- 示例 END -->
			
			<!-- 关键字模块 START -->
			<view class="mt18 uni_flex_row_between_center">
				<view class="uni_flex_row_align_left">
					<image src="../../static/icon/icon_keyword.png" style="width: 36rpx; height: 36rpx;"></image>
					<text class="ml4 uni_16px uni_color_666">可以试试</text>
				</view>
				
				<view class="uni_flex_row_align_right">
					<uni-icons type="loop" color="#9686FB" size="17" @click="queryStyles"></uni-icons>
					<text class="ml4 uni_13px change_next" @click="queryStyles">换一批</text>
				</view>
			</view>
			
			<view class="uni_flex_warp uni_flex_row_between mt8">
				<view v-for="(keyword,id) in keywords" v-bind:key="id" class="tag_item" @click="onClickKeyword(keyword)">{{keyword.name}}</view>
			</view>
			
			<view class="uni_flex_row_align_left mt20">
				<text class="uni_16px uni_color_666">高级设置</text>
				<switch style="transform: scale(0.6);" color="#9686FB" :checked="isShowHeightSetting" @change="setSettingOfShowHeight"></switch>
			</view>
			
			<!-- 画作风格 START -->
			<view class="uni_flex_row_between mt10" v-if="isShowHeightSetting">
				<view class="uni_flex_row_align_left">
					<image src="../../static/icon/icon_style.png" style="width: 34rpx;height: 34rpx;"></image>
					<text class="ml4 uni_15px uni_color_666">绘图风格</text>
					<text class="ml4 uni_11px uni_color_bbb">(可选,默认不限风格)</text>
				</view>
				<view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'style')">
					<text class="uni_14px uni_font_regular uni_color_app1">{{imageStyle.tag || '请选择风格'}}</text>
					<uni-icons type="right" color="#9686FB" class="ml6"></uni-icons>
				</view>
			</view>
			<!-- 画作风格 END -->
			
			<!-- 生成张数 START -->
			<view class="uni_flex_row_between mt12" v-if="isShowHeightSetting">
				<view class="uni_flex_row_align_left">
					<image src="../../static/icon/icon_number.png" style="width: 34rpx;height: 34rpx;"></image>
					<text class="ml4 uni_15px uni_color_666">图片生成张数</text>
					<text class="ml4 uni_11px uni_color_bbb">(可选,默认1张)</text>
				</view>
				<view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'number')">
					<text class="uni_14px uni_font_regular uni_color_app1">{{createAiNumber + ' 张' || '请选择张数'}}</text>
					<uni-icons type="right" color="#9686FB" class="ml6"></uni-icons>
				</view>
			</view>
			<!-- 生成张数 END -->
			
			<!-- 画作分辨率 START -->
			<view class="uni_flex_row_between mt12" v-if="isShowHeightSetting">
				<view class="uni_flex_row_align_left">
					<image src="../../static/icon/icon_ratio.png" style="width: 34rpx;height: 34rpx;"></image>
					<text class="ml4 uni_15px uni_color_666">分辨率</text>
					<text class="ml4 uni_11px uni_color_bbb">(可选,默认1024*1536)</text>
				</view>
				<view class="uni_flex_row_align_center select_button" @click="showPickerDataObj(true, 'ratio')">
					<text class="uni_14px uni_font_regular uni_color_app1">{{imageRatio.tag || '请选择分辨率'}}</text>
					<uni-icons type="right" color="#9686FB" class="ml6"></uni-icons>
				</view>
			</view>
			<!-- 画作分辨率 END -->
			
			<!-- 开始绘画按钮 -->
			
			<view class="uni_flex_row_align_center mt30">
				<view class="start_button able" @click="onCheckStatusAndShowDialog">
					<text class="uni_font_regular" style="letter-spacing: 2rpx;">{{isLogined ? 'Ai 绘画创作':'登录后使用Ai绘画功能'}}</text>
				</view>
				
				<uni-badge :text="freeNum" absolute="rightTop" size="normal" :offset="[5,5]">
					<view v-if="freeNum>0" class="start_test_btn" @click="showTestDialog(true)">
						免费体验
					</view>
					<view v-else class="start_test_btn_unable">
						暂不可用
					</view>
				</uni-badge>
			</view>
			
			<!-- 客服信息 -->
			<view class="customer uni_12px mt10 uni_color_999">
				<text @click="onCopy('729913920')">QQ交流学习群:729913920 (点击复制)</text>
			</view>
				
		</view>
		
		<!-- 数据选择弹框 -->
		<uni-popup type="bottom" ref="pickerDataObj">
			<view class="popup_dialog_style">
				<view class="popup_dialog_title">
					<text class="uni_color_999 uni_font_regular" @click="showPickerDataObj(false)">取消</text>
					<text class="uni_color_000 uni_font_medium" v-if="dialogType === 'number'">请选择画作生成张数</text>
					<text class="uni_color_000 uni_font_medium" v-else-if="dialogType === 'style'">请选择画作风格</text>
					<text class="uni_color_000 uni_font_medium" v-else-if="dialogType === 'ratio'">请选择画作分辨率</text>
					<text class="uni_color_222 uni_font_regular" @click="onPickerData('',true)">确定</text>
				</view>
				<view class="uni_flex_warp uni_flex_row_between dialog_data_view" v-if="dialogType === 'number'">
					<text
						v-if="dialogType === 'number'"
						v-for="(dateItem, index) in dialogData" v-bind:key="index" 
						class="style_item_big" 
						:class="tempImageNumber === dateItem ? 'style_checked':''"
						@click="onPickerData(dateItem)">
						{{dateItem}}张
					</text>
				</view>
				
				<view class="uni_flex_warp uni_flex_row_align_left" v-if="dialogType === 'style'">
					<text
						v-for="(ratio,index) in styles" v-bind:key="index" 
						class="style_item_big" 
						:class="tempImageStyle.id === ratio.id ? 'style_checked':''"
						@click="onPickerData(ratio)">
						{{ratio.tag}}
					</text>
				</view>
				
				<view class="uni_flex_warp uni_flex_row_between" v-if="dialogType === 'ratio'">
					<text
						v-for="(ratio,index) in ratios" v-bind:key="index" 
						class="style_item_big" 
						:class="tempImageRatio.id === ratio.id ? 'style_checked':''"
						@click="onPickerData(ratio)">
						{{ratio.tag}}
					</text>
				</view>
			</view>
		</uni-popup>	
		
		<!-- 开始AI绘图任务后的状态展示弹窗 -->
		<uni-popup ref="taskStatusDialog" type="center">
			<view class="task_status_dialog">
				<image src="../../static/img_ai_status_dialog.gif" style="width: 400rpx;height: 160rpx;"></image>
				<view class="uni_14px uni_font_regular">{{aiRequestStatusText}}</view>
				<view class="uni_flex_row_align_center uni_flex_warp mt10 ml10 mr10">
					<view v-for="(wait,wid) in waitTime" v-bind:key="wid" class="white_point"></view>
				</view>
				<view class="mt4 uni_16px uni_font_medium">{{waitTime.length}}s</view>
			</view>
		</uni-popup>
			
		<uni-popup ref="dialogTest" type="bottom">
			<view class="test_dialog uni_flex_col_align_center">
				<view class="uni_font_medium uni_17px uni_color_333 mt10 mb15">免费体验说明</view>
				
				<text class="uni_15px uni_color_999 ml5 mr5" style="letter-spacing: 2rpx;">
					全系统今日总共还
					<text class="uni_font_regular uni_color_666">剩余{{freeNum}}次免费体验机会,</text>
					系统每天晚上20点下放随机数量的免费体验次数,当天用完即止,每个账号可先到先得免费使用一次ai绘图功能(注:为避免占用,每个账号每天有且只有一次免费体验机会)
				</text>
				
				<text class="dialog_btn bg1 mt30" @click="onConfirmTest">立即免费创作</text>
				<text class="dialog_btn bg3 mt20 mb15" @click="showTestDialog(false)">取消</text>
			</view>
		</uni-popup>	
		
		<!-- 提示信息弹窗 -->
		<uni-popup ref="message" type="message">
			<uni-popup-message type="error" :message="messageText" :duration="3200"></uni-popup-message>
		</uni-popup>
		
		<uni-popup ref="confirmDialog" type="center">
			<view class="uni_flex_col_align_center">
				<view class="confirm_dialog_bg mb10">
					<view class="uni_18px uni_color_000 uni_font_medium mt10 mb15">温馨提示</view>
					
					<view class="uni_16px uni_color_666 ml10 mr10" style="letter-spacing: 2rpx;">
						<text>您正在使用易绘的Ai绘画功能,此功能按次收费,本次操作将从您的易绘账号上</text>
						<text class="uni_16px uni_font_regular ml2 uni_color_red">扣除 {{price * createAiNumber}} 钻石</text>
						<text class="ml2">({{createAiNumber}}张图) 是否已知悉并确认?</text>
					</view>
					
					<!-- <view class="uni_16px uni_color_666" v-if="createAiNumber <= 1">您正在使用Ai绘画功能,AI绘图功能单次需扣除 {{price}} 钻石,是否确定?</view> -->
					<!-- <view class="uni_16px uni_color_666" v-else>您正在使用Ai绘画功能,当前您设置了同时生成{{createAiNumber}}张图片,本次需扣除 {{price * createAiNumber}} 钻石,是否确定并开始绘画?</view> -->
					<text class="dialog_btn bg1 mt30 mb20" @click="onClickStartButton('pay')">确认并开始绘画</text>
					<text v-if="couponNum>0" class="dialog_btn bg2 mb25" @click="onClickStartButton('coupon')">
						使用免费券抵扣<text class="uni_12px uni_color_fff uni_font_light">({{couponNum}}张)</text>
					</text>
					<!-- <text class="dialog_btn bg3 mt20 mb15" @click="showTestDialog(false)">取消绘画</text> -->
				</view>
				<uni-icons type="close" size="45" color="#ffffff" @click="showAiConfirmDialog(false)"></uni-icons>
			</view>
						
		</uni-popup>
	</view>
</template>

<script>
	import api from '../../common/network/api/api.js';
	import dataBase from '../../common/database.js';
	import UI from '../../common/UI.js';
	import handle from '../../common/handle.js';
	
	export default {
		data() {
			return {
				maxNum: 100,
				inputContent: '', // 输入的描述内容
				errorTip: '', // 错误提示
				exampleObj: {}, // 示例
				tempImageRatio: {},
				tempImageStyle: {},
				tempImageNumber: 1,
				imageStyle: {},
				imageRatio: {},
				keywords: [], // 关键字列表
				// simpleKeyWords:[], // 简单列表的关键字
				styles: [], // 风格列表
				ratios: [] ,// 分辨率列表
				countAiTask: undefined, // ai绘图状态弹窗的计时器
				waitTime: [], // ai绘图状态弹窗的 等待时间计时
				waiting: '', // 预估等待时间
				ableReload: true, // 是否可以重新加载
				aiRequestStatusText: '', // AI请求
				
				messageTimeoutTask: undefined, // 临时消息的计时器对象
				
				aiStatus: 0, // AI绘图状态 0|队列中  1|已完成
				aiId: '', // 唯一ID
				taskNum: 0,
				createAiNumber: 1, // 申请AI绘画的图片数量
				
				isLogined: false,
				price: 0,
				
				dialogType: '',
				dialogData: [],
				
				animationData:{},
				freeNum: 0, // 系统免费ai体验次数
				
				isShowHeightSetting: false,
			}
		},
		onShow() {
			this.init();
			this.getSettingOfShowHeight();
			console.error("环境:",process.env.NODE_ENV)
		},
		onLoad() {
			uni.$on('networkResume',()=>{
				this.init();
			});
		},
		onUnload() {
			uni.$off('networkResume');
			this.showTaskStatusDialog(false);
		},
		methods: {
			getSettingOfShowHeight(){
				let temp = dataBase.queryStorage('heightSetting') || false;
				this.isShowHeightSetting = temp;
				return temp;
			},
			setSettingOfShowHeight(e){
				this.isShowHeightSetting = e.detail.value;
				dataBase.insertStorage('heightSetting',e.detail.value);
			},
			showAiConfirmDialog(show){
				if(show)
					this.$refs.confirmDialog.open();
				else
					this.$refs.confirmDialog.close();
			},
			// 确定体验
			onConfirmTest(){
				this.showTestDialog(false);
			},
			showTestDialog(show){
				if(show){
					this.onCheckStatusAndShowDialog('free');
					// this.$refs.dialogTest.open();
				}else{
					this.$refs.dialogTest.close();
				}
			},
			init(){
				// 允许重新加载
				this.ableReload = true;
				this.isLogined = dataBase.queryLoginStatus();
				
				this.queryStyles(); // 查询风格/分辨率等
				this.queryExample(); // 查询示例
				
				if(this.isLogined){
					this.queryTaskNum(); // 查询任务数据
					api.doPost({action:'queryUnreadMessageNumber'}).then(res=>{
						let unread = res.data.unread;
						unread > 0 ? uni.showTabBarRedDot({index: 4}) :  uni.hideTabBarRedDot({index: 4});
					});
				}				
			},
			gotoSchool(){
				console.error("前往学院");
				uni.navigateTo({
					url:'/pages_sub/center/pages/school/school'
				});
			},
			// 前往任务列表页面
			gotoTaskList(){
				uni.navigateTo({
					url:'/pages_sub/ai/pages/ai-task-list/ai-task-list'
				});
			},
			// 查询任务数量
			queryTaskNum(){
				api.doPost({action:'queryAITaskNumOrList'}).then(res=>{
					this.taskNum = res.data.num;
					this.couponNum = res.data.freeConpon;
				});
			},
			// 点击主类关键字
			onClickKeyword(item){
				if(this.inputContent && this.inputContent.length > 0){
					let lastWord = this.inputContent.substring(this.inputContent.length - 1, this.inputContent.length);
					if(lastWord === ','){
						this.inputContent = this.inputContent.substring(0, this.inputContent.length - 1);
					}
				}
				
				this.inputContent += (this.inputContent ? ',':'') + item.name;
				// 限制字数
				if(this.inputContent.length > this.maxNum){
					this.inputContent = this.inputContent.substring(0, this.maxNum);
				}
			},
			// 选择风格
			onPickerData(style, confirm){
				if(confirm){
					this.showPickerDataObj(false);
					switch(this.dialogType){
						case 'number': this.createAiNumber = this.tempImageNumber; break;
						case 'style': this.imageStyle = this.tempImageStyle; break;
						case 'ratio': this.imageRatio = this.tempImageRatio; break;
					}
				}else{
					switch(this.dialogType){
						case 'number': this.tempImageNumber = style; break;
						case 'style': this.tempImageStyle = style; break;
						case 'ratio': this.tempImageRatio = style; break;
					}
				}
			},
			// 显示和隐藏任务进度弹窗
			showTaskStatusDialog(show){
				const $that = this;
				
				if(this.countAiTask){
					clearInterval(this.countAiTask);
				}
				
				if(show){
					this.waitTime = [];
					
					$that.countAiTask = setInterval(()=>{
						$that.waitTime.push("");
						// 是否可以重新加载
						if($that.ableReload){
							$that.queryAiResult();
						}
					}, 2000);
					$that.$refs.taskStatusDialog.open();
				}else{
					$that.inputContent = '';
					$that.$refs.taskStatusDialog.close();
				}
			},
			// 检查条件以及展示价格确认
			onCheckStatusAndShowDialog(mode){
				const $that = this;
				// 未登录
				if(!this.isLogined){
					uni.showModal({
						title: '提示',
						content: '需要登录才能使用此功能,是否前往登录?',
						confirmText: '前往登录',
						cancelText: '暂不登录',
						complete(res) {
							if(res.confirm){
								uni.navigateTo({
									url:'/pages_sub/login/pages/login/login'
								});
								return "NO";
							}
						}
					});
					return "NO";
				}
				
				if(!this.inputContent){
					this.messageText = '请在下方输入画作的文字描述'
					this.$refs.message.open();
					return "NO";
				}
				
				if(this.price > 0 && mode !== 'free'){
					// 开始ai的确认弹窗
					this.showAiConfirmDialog(true);
					return "NO";
				}
				
				$that.onClickStartButton(mode);
				return "OK"
			},
			// 开始AI绘画
			onClickStartButton(mode){
				// 隐藏确认对话框
				this.showAiConfirmDialog(false);
				
				uni.showLoading({
					title:'正在提交云端',
				});
				
				let data = {
					action:'queryAiTask',
					text: this.inputContent, 
					ratio: this.imageRatio.tag, 
					style: this.imageStyle.tag, 
					num:this.createAiNumber,
					mode: mode // 免费  付费
				};
				
				api.doPost(data).then(res=>{
					uni.hideLoading();
					if(res.code === 208){
						handle.gotoRecharge(res.message);
						return;
					}
					
					this.showTaskStatusDialog(true);
					this.aiStatus = res.data.status;
					this.aiId = res.data.aid;
					
					this.aiRequestStatusText = '已成功连接云端';
					
					// 队列中
					if(this.aiStatus === 0){
						this.waiting = res.data.waiting;
						this.aiRequestStatusText = "正在刻画绘图...,预估时长【"+res.data.waiting+"】";
						
						this.queryTaskNum();
					}
					// 已完成
					else{
						this.aiRequestStatusText = '已完成AI绘图作画任务';
						this.showTaskStatusDialog(false);
						
						console.error("AI绘图完成",res.data);
						uni.navigateTo({
							url:'/pages_sub/ai/pages/preview-ai-img/preview-ai-img?data='+JSON.stringify(res.data)
						});
					}
				},err=>{
					uni.hideLoading();
				});
			},
			// 单纯查询ai绘图状态
			queryAiResult(){
				this.ableReload = false;
				console.error("AI任务查询开始");
				api.doPost({action:'queryAiResult',aid:this.aiId}).then(res=>{
					console.error("AI任务查询结果:",res);
					let code = res.code;
					
					if(code === 200){
						this.showTaskStatusDialog(false);
						
						uni.navigateTo({
							url:'/pages_sub/ai/pages/preview-ai-img/preview-ai-img?data='+JSON.stringify(res.data)
						});
					}else{
						this.ableReload = true;
					}
				}, err=>{
					console.error("AI任务查询失败:",err);
					this.ableReload = false;
					this.showTaskStatusDialog(false);
				}).catch(e=>{
					console.error("AI任务发生异常:",e);
				});
			},
			// 清除内容
			onClear(){
				this.inputContent = '';
			},
			// 复制内容
			onCopy(text){
				uni.setClipboardData({
					data: this.inputContent,
					success() {
						uni.showToast({
							title:'复制成功~',
							icon:'success'
						});
					}
				});
			},
			// 显示/关闭数据选择弹窗
			showPickerDataObj(show, type){
				if(show){
					this.dialogType = type;
					
					switch(this.dialogType){
						case 'number': this.dialogData = [1,2,3,4,5,6]; break;
						case 'style': this.dialogData = this.styles; break;
						case 'ratio': this.dialogData = this.ratios; break;
					}
					
					this.$refs.pickerDataObj.open();
				}else{
					this.$refs.pickerDataObj.close();
				}
			},
			// 点击示例文字时,会把示例内容带入到输入框里
			onClickExample(){
				const $that = this;
				
				// 判断示例是否存在内容
				if(this.exampleObj.content){
					// 如果输入框内已有内容,就弹出对话框提示用户是否确定要用示例替换
					if(this.inputContent){
						uni.showModal({
							title:'温馨提示',
							content:"是否确定要用示例的内容「替换」输入框描述内容?",
							confirmText: '确认替换',
							cancelText: '取消',
							complete(res) {
								if(res.confirm){
									$that.inputContent = $that.exampleObj.content;
								}
							}
						})
					}else{
						this.inputContent = this.exampleObj.content;
					}
				}
			},
			// 加载/刷新示例
			queryExample(){
				let data = {
					action:'queryExample',
				}
				
				// 判断是否有id
				if(this.exampleObj && this.exampleObj.id){
					data.id = this.exampleObj.id;
				}
				
				api.doPost(data).then(res=>{
					this.exampleObj = res.data;
				});
			},
			// 查询风格列表
			queryStyles(){
				api.doPost({action:'queryStyles'}).then(res=>{
					this.styles = res.data.styles || [];
					this.ratios = res.data.ratios || [];
					this.keywords = res.data.keywords || [];
					this.price = res.data.price || 0;
					this.freeNum = res.data.freeNum;
					
					if(this.styles && this.styles.length > 0){
						this.imageStyle = this.styles[0];
						this.tempImageStyle = this.styles[0];
					}
					
					if(this.ratios && this.ratios.length > 0){
						this.imageRatio = this.ratios[0];
						this.tempImageRatio = this.ratios[0];
					}
				});
			},
			startVoice() {
				if(!this.isLogined){
					UI.gotoLoginWithComfirmDialog();
					return;
				}
				
				// #ifdef APP-PLUS
				// 开始语音识别
				const $that = this;
				plus.speech.startRecognize({
					engine: 'baidu'
				}, (e => {
					if (e) {
						$that.inputContent += e;
						
						// 限制字数
						if($that.inputContent.length > $that.maxNum){
							$that.inputContent = $that.inputContent.substring(0, $that.maxNum);
						}
					}
				}), (e => {
					if (e.code === 1310722) {
						uni.showToast({
							title: '抱歉,没有听清,请您提高音量再试一遍吧',
							icon: 'none'
						})
					}
				}))
				// #endif
			}
		}
	}
</script>

<style>
@import url('index.css');
</style>

三、成品展示图

ai绘图主页
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
下载链接: Android apk
苹果链接: AppStore链接

三、登录页

<template>
	<view class="page pageStyle">
		<view class="uni_flex_col_align_center flexX margin-top-100">
			<view class="uni_flex_col_align_center margin-bottom-50">
				<image src="/static/app_logo200.png" class="iconimg"></image>
				
				<view class="app_name uni_font_medium uni_25px">{{appName}}</view>
				<view class="app_version uni-font-regular">{{version}}</view>
			</view>
			
			<!-- #ifdef MP-WEIXIN -->
			<button @click="getuserinfo" withCredentials="true" class="btn_func uni_flex_row_align_center uni_font_medium">微信授权登陆</button>
			<view class="btn_func_un uni_flex_row_align_center uni-font-regular" @click="finishPage">暂不登录</view>
			<!-- #endif -->
			
			<!-- #ifdef APP-PLUS -->
			<view v-if="isShowWX" class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('app-wx')">
				<image class="icon_wechat" src="/pages_sub/login/static/icon_share_wx.png"></image>
				<text style="letter-spacing: 4rpx;">微信授权登录</text>
			</view>

			<view v-if="isCanPhoneNumberLogin" class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('mobile')">
				<uni-icons type="phone-filled" color="#E00300" size="20"></uni-icons>
				<view style="margin-left: 15rpx;">手机号一键登录</view>
			</view>

			<view class="btn_func_bg uni_flex_row_align_center uni_font_medium" @click="doLoginAction('apple')" v-if="isApple">
				<image class="icon_apple" src="/pages_sub/login/static/icon_apple.png"></image>
				<view>通过 Apple 登录</view>
			</view>
			
			<view class="btn_func_un uni_flex_row_align_center uni-font-regular" @click="finishPage">暂不登录</view>
			<!-- #endif -->
			
			<!-- #ifdef H5 -->
			<!-- 下载按钮 -->
			<view>
				<view class="uni_flex_row_align_center download_btn bbg1" @click="download(1)">
					<image style="width: 45rpx;height: 45rpx;" class="mr4" src="https://mp-4600ec1f-16af-433c-a045-130f8b08315f.cdn.bspapp.com/cloudstorage/3122fd08-63b0-42cd-91f1-f11421e72389.png"></image>
					<text class="uni_15px uni_font_regular" style="letter-spacing: 2rpx;">下载App (安卓)</text>
				</view>
				
				<view class="uni_flex_row_align_center download_btn bbg2" @click="download(2)">
					<image style="width: 40rpx;height: 40rpx;" class="mr4" src="https://mp-4600ec1f-16af-433c-a045-130f8b08315f.cdn.bspapp.com/cloudstorage/a8074492-ae50-42ad-ba0e-5399354ad9c6.png"></image>
					<text class="uni_15px uni_font_regular" style="letter-spacing: 2rpx;">下载App (苹果)</text>
				</view>
				
				<view class="uni_flex_col_align_center mt15">
					<image style="width: 288rpx;height: 288rpx;" class="mr4" src="https://luanqing.oss-cn-shanghai.aliyuncs.com/icon/yihui/min_code_yihui.jpeg"></image>
					<text class="uni_15px uni_font_regular mt6" style="letter-spacing: 2rpx;">微信小程序码</text>
				</view>
				
			</view>
			<!-- #endif -->
		</view>
		
		<!-- #ifdef APP-PLUS -->
		<view class="right uni_flex_col_align_center flexS">
			<view class="uni_flex_row_align_center uni_14px uni-font-regular">
				<checkbox style="transform: scale(0.65);" color="#8256f6" :checked="agreeState" @click="doAgree"></checkbox>
				<view class="uni-font-light">已阅读并同意</view>
				<view class="privacy uni_font_medium" @click="gotoUserAgreen">《用户服务协议》</view>
				<view class="privacy uni_font_medium" @click="gotoPrivacy">《隐私政策》</view>
			</view>
			
			<view class="uni_13px mt6">上海栾青网络科技有限公司出品</view>
			<view class="uni_13px mb15">Copyright © 2023</view>
		</view>
		<!-- #endif -->
	</view>
	
</template>

<script>
	// #ifdef APP-PLUS
	const univerifyManager = uni.getUniverifyManager()
	// #endif
	
	import api from '../../../../common/network/api/api.js';
	import dataBase from '../../../../common/database.js';
	
	export default{
		data(){
			return{
				agreeState: false,
				version:"v1.0",
				appName: '易绘',
				fromSource: 'index', // 来源,如果是index,则登录后返回主页,否则返回上一级
				code:undefined,
				isShowWX:true,
				isApple:false,
				isCanPhoneNumberLogin: false,
			}
		},
		onLoad(opt) {
			// #ifdef MP-WEIXIN
			const $that = this;
			
			this.fromSource = opt.fromSource || 'index';
			
			uni.login({
				success(res) {
					$that.code = res.code;
				},
				fail(res) {
				},
			})	
			// #endif
		},
		onShow() {
			const $that = this;
			$that.version = 'v'+ uni.getSystemInfoSync().appVersion;

			// #ifdef APP-PLUS
			if(plus.runtime.isApplicationExist({pname:'com.tencent.mm',action:'weixin://'})){
				console.log("微信应用已安装");
				$that.isShowWX = true;
			}else{
				console.log("微信应用未安装");
				$that.isShowWX = false;
			}
			
			uni.preLogin({
				provider:'univerify',
				success: (suc) => {
					this.isCanPhoneNumberLogin = true;
					if(univerifyManager){
						univerifyManager.preLogin();
					}
				},
			});
			
			this.isApple = uni.getSystemInfoSync().platform == 'ios';
			// #endif
		},
		methods:{
			download(type){
				if(type === 1){
					window.location.href = dataBase.appDownloadUrl;
				}else if(type === 2){
					window.location.href = 'https://apps.apple.com/cn/app/%E6%98%93%E7%BB%98ai%E4%BD%9C%E7%94%BB/id1670258950';
				}
			},
			finishPage(){
				uni.navigateBack({delta:1});
			},
			// 登录动作中枢
			doLoginAction(type){
				if(!this.agreeState){
					const $that = this;
					uni.showModal({
						title:'温馨提示',
						content:'您是否已阅读并同意 《用户服务协议》、 《隐私政策》?',
						confirmText:'同意并登录',
						cancelText:'拒绝',
						complete: (res) => {
							if(res.confirm){
								$that.agreeState = true;
								$that.doLoginAction(type);
							}
						}
					});
					return;
				}
				
				switch(type){
					case 'apple': this.login4ApplePhone(); break;
					case 'app-wx': this.login4AppWx(); break;
					case 'mobile': this.login4Mobild(); break;
				}
			},
			gotoUserAgreen(){
				uni.navigateTo({
					url:'/pages_sub/center/pages/webview/webview?url='+dataBase.appAgreement+'&title=用户协议'
				});
			},
			gotoPrivacy(){
				uni.navigateTo({
					url:'/pages_sub/center/pages/webview/webview?url='+dataBase.appPrivacy+'&title=隐私协议'
				});
			},
			doAgree(){
				this.agreeState = !this.agreeState;
			},
			// 手机号一键登录
			login4Mobild(){
				const $that = this;
				// 一键登录必须是手机使用流量的前提下才能获取到手机号码,用Wi-Fi联网时无法获取到手机号码,同时如果是双卡手机,获取到的手机号码是默认移动数据的那个手机卡的号码。
				// #ifdef APP-PLUS
				// 预登录 START
				// 1.提高一键登录的加载速度  
				// 2.判断一键登录环境是否可用
				univerifyManager.preLogin();
				
				// 调用一键登录弹框
				univerifyManager.login({
					univerifyStyle: {
					fullScreen:false,
					icon: {
						path:'/static/app_logo200.png'
					},
					privacyTerms:{
						defaultCheckBoxState: false,
						checkBoxSize: 14,
						privacyItems:[
							{  
								"url": dataBase.appAgreement, 
								"title": "用户服务协议"  
							},
							{  
								"url": dataBase.appPrivacy, 
								"title": "隐私协议"  
							}  
						]
					},
					authButton:{
						normalColor:"#9686FB",
						disabledColor:"#AAAAAA"
					},
				    otherLoginButton: {  
						visible: true, // 是否显示其他登录按钮,默认值:true  
						normalColor: "", // 其他登录按钮正常状态背景颜色 默认值:透明 
						highlightColor: "", // 其他登录按钮按下状态背景颜色 默认值:透明 
						textColor: "#656565", // 其他登录按钮文字颜色 默认值:#656565  
						title: "其他登录方式", // 其他登录方式按钮文字 默认值:“其他登录方式”  
						borderColor: "",  //边框颜色 默认值:透明(仅iOS支持)  
						borderRadius: "0px" // 其他登录按钮圆角 默认值:"24px" (按钮高度的一半)
					},  
				},
				success (auth) {
					uniCloud.callFunction({
						name:'getPhoneNumber',
						data:{ openid: auth.authResult.openid, access_token: auth.authResult.access_token},
						success(cloudRes) {
							let {code, phoneNumber} = cloudRes.result;
							console.error("手机号:",phoneNumber);
							
							if(code === 200){
								const data = {
									name: '手机用户',
									unionId: phoneNumber
								};
								$that.submitLoginData(data);
								univerifyManager.close();
							}else{
								univerifyManager.close();
							}
						},
						fail: (err) => {
							console.error("云函数失败:",err);
						}
					})
				},
				fail(res) {
					// 点击其他登录方式
					if(res.code === 30002){
						univerifyManager.close();
					}
				}
				})
			  // #endif
			},
			// 苹果登录
			login4ApplePhone(){
				const $that = this;
				uni.login({  
				    provider: 'apple', 
				    success(loginRes){  
				        // 登录成功  
				        uni.getUserInfo({  
				            provider: 'apple',  
				            success(res) {  
								// 苹果登录成功:{"errMsg":"getUserInfo:ok","userInfo":{"openId":"001465.6449e1ad2e46401488d67bae89d79c8a.1706","fullName":{"familyName":"许","giveName":"仕永","givenName":"仕永"},"email":"835588741@qq.com","authorizationCode":"c9acf4dd2a2a9491a93d158a7669b4165.0.mruwv.b0hncISXQbHMGinz5_f3vg","identityToken":"eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmx1YW5xaW5nLmJhYnkiLCJleHAiOjE2MjA5MjU2MTIsImlhdCI6MTYyMDgzOTIxMiwic3ViIjoiMDAxNDY1LjY0NDllMWFkMmU0NjQwMTQ4OGQ2N2JhZTg5ZDc5YzhhLjE3MDYiLCJjX2hhc2giOiJqVG9MWHFKd3BBemxxSlNmV2xjWFBBIiwiZW1haWwiOiI4MzU1ODg3NDFAcXEuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwiYXV0aF90aW1lIjoxNjIwODM5MjEyLCJub25jZV9zdXBwb3J0ZWQiOnRydWUsInJlYWxfdXNlcl9zdGF0dXMiOjJ9.nAHGpGDpjALPmz-vReHjIAV3IyrYJTrLxHmLDqTd2AShYH6XZW3Qh-27aAgbWjg8_3w4Ex22Tpv8_NhKNTAIrHjCNMN5jnw6WkXL4-utKTZi4mlX4p_WiPpkZSa7e0cJZYKrN5b3UEiI1Vf6yu8b5TeKz4E7cg1Iq6RfFNmXLYJmt92WRtwSPexinTySjVvZmxGZ-_7nnh-TowHXjedLBsUsdB7oRlp1xSRpRQm78YKnibwmF2__iejPTmKL_WwOTXXBsg4NrF5h7rFS0Z7jGvA8WPziJxoaeHDP_j1Iw2pcmfzMLA7FhSXlkcYd38To2Wv01kOma0fLlfIm8JovIg","realUserStatus":2}} at pages/user/login/login.vue:93 __ERROR 
								console.error("苹果登录成功:",res?.userInfo?.fullName?.giveName || "苹果用户",res?.userInfo?.openId);
								
				                // 获取用户信息成功  
								const data = {
									name: res?.userInfo?.fullName?.giveName || "苹果用户",
									unionId: res?.userInfo?.openId
								};
								$that.submitLoginData(data);
				            }  
				        })  
				    },  
				});  
			},
			// App端微信
			login4AppWx(){
				const $that = this;

				uni.login({
				  provider: 'weixin',
				  success: function (loginRes) {
					  uni.getUserInfo({
					  	success(res) {
					  		const data = {
					  			avatar:res.userInfo.avatarUrl,
					  			name:res.userInfo.nickName,
					  			gender:res.userInfo.gender,
					  			unionId: res.userInfo.unionId,
					  		};
							
					  		$that.submitLoginData(data);
					  	},
					  })
				  },
				});
			},
			// 提交登录信息给后端
			submitLoginData(data){
				data.action = "submitLoginData";
				api.doPost(data).then(res=>{
					let temp = res.data;
					dataBase.insertUUIDAtStorage(temp.uuid);
					dataBase.insertTokenAtStorage(temp.token);
					this.finishPage();
				});
			},
			// 小程序专用 2
			getuserinfo(){
				let $that = this;
				let code = $that.code;		
				uni.showToast({
					title:'正在请求中',
					icon:'none'
				});
				
				uni.getUserProfile({
					desc:'用于登录获取昵称头像',
					success(res) {
						console.error("获取的用户资料:",JSON.stringify(res));
						
						const data = {
							action:'submitLoginDataMP',
							code:code,
							avatar:res.userInfo.avatarUrl,
							name:res.userInfo.nickName,
							gender:res.userInfo.gender,
						};
						
						api.doPost(data).then(response=>{
							dataBase.insertTokenAtStorage(response.data.token);
							dataBase.insertUUIDAtStorage(response.data.uuid);
							$that.finishPage();
						});
					}
				})
			},
		}
	}
</script>

<style>
	@import url("login.css");
</style>

四、头像制作

<template>
	<view>
		<uni-nav-bar :statusBar="true" :border="false" :fixed="true" :leftWidth="400" :rightWidth="200">
			<template v-slot:left class="uni_flex_row_align_left uni_14px uni_font_medium">
				<text class="button_import" @click="importImageBg">导入图片</text>
				<text class="button_save" @click="onSave2PhoneStorage">
					保存本地
					<!-- <text class="uni_12px" style="color: #E00300;">免费</text> -->
				</text>
			</template>
			<!-- #ifndef MP -->
			<template v-slot:right>
				<view class="uni_flex_row_align_center" @click="onClickHelp(true)">
					<uni-icons type="help-filled" size="22" color="#A2A3A4"></uni-icons>
					<view class="uni_12px uni_color_999">帮助</view>
				</view>
			</template>
			<!-- #endif -->
		</uni-nav-bar>
		
		<view class="uni_flex_col_align_center" v-if="isShowCanvas" style="background-color: #ffffff;position: sticky;top: 140rpx;z-index: 999;">
			<canvas id="canvasId" canvas-id="canvasId" class="avatar_panel" @touchstart="onTouchStart" @touchmove="onTouchMove"></canvas>
		</view>
		
		<!-- 缩放、旋转操作栏 START -->
		<view v-if="moveElement.url_mp || moveElement.url" class="uni_flex_row_align_center mt10">
			<view class="tag_fun_text2" @click="onImgTagRotate(-1)">
				<image class="tag_fun_img" src="../../static/icon/icon_left.png"></image>
			</view>

			<view class="tag_fun_text ml12 mr8" @click="onImgTagScan(-1)">缩小</view>
			
			<!-- #ifdef MP -->
			<!-- <image class="selected_tag" :src="moveElement.url_mp" mode="widthFix"></image> -->
			<!-- #endif -->
			
			<view class="tag_fun_text ml10 mr10" @click="onImgTagRemove">移除</view>
			
			<!-- #ifndef MP -->
			<!-- <image class="selected_tag" :src="moveElement.url" mode="widthFix"></image> -->
			<!-- #endif -->
			
			<view class="tag_fun_text ml8 mr12" @click="onImgTagScan(1)">放大</view>
			
			<view class="tag_fun_text2" @click="onImgTagRotate(1)">
				<image class="tag_fun_img" src="../../static/icon/icon_right.png"></image>
			</view>
		</view>
		<!-- 缩放、旋转操作栏 END -->
		
		<view class="uni_flex_row_align_left ml15 mt25">
			<image src="/static/icon/icon_color_piacker.png" style="width: 30rpx;height: 30rpx;"></image>
			<text class="uni_14px uni_color_222 ml8 uni_font_medium">背景颜色</text>
		</view>
		
		<view class="uni_flex_warp uni_flex_row_between ml15 mr15 mt12">
			<view v-for="(color,id) in colorList" class="bg" v-bind:key="id" :style="'background-color:#'+color" @click="onClickColorItem(color)"></view>
		</view>
		
		<view class="input_view ml15 mr15" v-if="false">
			<view class="uni_flex_row_align_left uni_font_regular uni_17px">
				<text class="mr2">#</text>
				<input class="input_style" maxlength="6" v-model="customColor" placeholder="请输入6位十六进制码" />
			</view>
			<view class="uni_flex_row_align_right" @click="onClickColorItem()">
				<text class="uni_green_209B5C uni_14px uni_font_medium">使用自定义</text>
				<view class="color_tag ml10" :style="'background-color:#'+customColor"></view>
			</view>
		</view>
		
		<view class="uni_flex_row_align_left ml15 mt25">
			<image src="/static/icon/icon_img_tag.png" style="width: 30rpx;height: 30rpx;"></image>
			<text class="uni_14px uni_color_222 ml8 uni_font_medium">装饰点缀图案</text>
		</view>
		
		<!-- 点缀的图案 -->
		<swiper class="uni_flex_col_align_center ml14 mr14 mt12" style="height: 365rpx;" :indicator-dots="true" indicator-active-color="#8256f6">
			<swiper-item v-for="(page,pid) in imgTag" v-bind:key="pid" style="width: 690rpx;">
				<view class="uni_flex_warp">
					<view class="tag_item" v-for="(tag, tid) in page" v-bind:key="tid" @click="onPickerImgTag(tag,tid)">
						<image class="tag_size" :src="tag.url"></image>
					</view>
				</view>
			</swiper-item>
		</swiper>
		
<!-- 		<view class="page_tip_view mt20" v-if="false">
			<text class="uni_13px">三步自定义头像制作</text>
			<text class="mt4 uni_12px">1.选择背景,添加头像的背景主图</text>
			<text class="mt2 uni_12px">2.点击添加装饰点缀的图案</text>
			<text class="mt2 uni_12px">3.点击保存,即可保存到手机(相册查看)</text>
		</view> -->
		
		<uni-popup ref="showHelpDialog" type="top" :isMaskClick="false">
			<view class="popup_help_view">
				<view class="page_tip_view mt40">
					<text class="uni_13px">三步自定义头像制作</text>
					<text class="mt4 uni_12px">1.选择背景,添加头像的背景主图</text>
					<text class="mt2 uni_12px">2.点击添加装饰点缀的图案</text>
					<text class="mt2 uni_12px">3.点击保存,即可保存到手机(相册查看)</text>
				</view>
				<view style="height: 40rpx;"></view>
				<view class="mt18 uni_15px button_round_style" @click="onClickHelp(false)">好的</view>
			</view>
		</uni-popup>
	</view>
</template>

<script>
	import api from '../../common/network/api/api';
	import dataBase from '../../common/database.js';
	
	export default {
		data() {
			return {
				isShowCanvas: true,
				// tagImages:[],
				
				drawTag:[], // 绘制在画布上的点缀图案列表
				imgTag:[],  // 可选的点缀图案
				canvasBG:'', // 画布的背景主图
				moveElement:{}, // 当前选中的元素
				canvasBgColor:'#ffffff', // 画布的背景颜色上色
				customColor:'FFFFFF', // 自定义颜色吗
				canvasSize: 231,
				colorList:["9686FB","6BADFF","77C38F","FFE16B","FF9F6B","FF6B6B"],
			}
		},
		onShow() {
			this.queryTags();
			
			this.isLogin = dataBase.queryLoginStatus();
			if(this.isLogin){
				api.doPost({action:'queryUnreadMessageNumber'}).then(res=>{
					let unread = res.data.unread;
					unread > 0 ? uni.showTabBarRedDot({index: 4}) :  uni.hideTabBarRedDot({index: 4});
				});
			}
		},
		methods: {
			onImgTagRemove(){
				let id = -1;
				this.drawTag.forEach((item,itemid)=>{
					if(item.id === this.moveElement.id){
						id = itemid;
					}
				});
				
				if(id != -1){
					this.drawTag.splice(id,1);
				}
				
				this.moveElement = {};
				this.drawCanvas();
			},
			// 保存到本地手机存储
			onSave2PhoneStorage(item){
				uni.canvasToTempFilePath({canvasId:'canvasId',success:(res)=>{
					// 保存到手机本地存储
					uni.saveImageToPhotosAlbum({filePath: res.tempFilePath,success:(res)=>{
						uni.showToast({
							title:'已下载到手机(可在相册查看)',
							icon:'none'
						});
					}});
				}});
			},
			// 导入头像主图
			importImageBg(){
				// #ifndef H5
				uni.chooseImage({
					count:1,
					crop: {width: 1000,height:1000,quality:100},
					success: (res) => {
						this.canvasBG = res.tempFilePaths[0];
						this.drawCanvas();
					}
				})
				// #endif
				
				// #ifdef H5
				uni.chooseFile({
					count:1,
					extension:['.png','.jpg'],
					complete: (res) => {
						console.error("选择成功:",res);
					}
				});
				// #endif
			},
			// 显示帮助弹窗
			onClickHelp(show){
				this.isShowCanvas = !show;
				if(show){
					this.$refs.showHelpDialog.open();
				}else{
					this.$refs.showHelpDialog.close();
				}
			},
			// 查询图案标签列表
			queryTags(){
				let tagData = dataBase.queryStorage("app_tag_lsit");
				if(tagData && tagData.date > (new Date().getTime())){
					this.imgTag = tagData.list;
				}else{
					api.doPost({action:'queryImageTag'}).then(res=>{
						let temp = res.data || [];
						
						let pageSize = 18;
						let pageNum = temp.length / 18 + (temp.length % 18 > 0 ? 1:0);
						pageNum = Math.floor(pageNum);
						
						this.imgTag = [];
						
						for(let i = 0 ; i < pageNum; i++){
							let sub; 
							if(i < pageNum - 1){
								sub = temp.slice(i * pageSize, pageSize);
							}else{
								sub = temp.slice(i * pageSize, i * pageSize + temp.length % 18);
							}
							
							this.imgTag.push(sub);
						}
						
						let insertData = { list: this.imgTag, date: (new Date().getTime() + 18000000) };
						dataBase.insertStorage('app_tag_lsit', insertData);
					})
				}
			},
			// 缩放
			onImgTagScan(mode){
				if(this.moveElement && this.moveElement.url){
					this.moveElement.width += (5 * mode); 
					this.moveElement.height += (5 * mode); 
				}	
				
				this.drawCanvas();
			},
			// 旋转
			onImgTagRotate(mode){
				if(this.moveElement && this.moveElement.url){
					if(this.moveElement.degree >= 360){
						this.moveElement.degree = mode === -1 ? 350:10;
					}else if(this.moveElement.degree <= 0){
						this.moveElement.degree = mode === -1 ? 360:10;
					}else{
						this.moveElement.degree += (10 * mode); 
					}
				}
				
				this.drawCanvas();
			},
			// 选择饰品图标
			onPickerImgTag(item,id){
				item.id = id;
				item.x = 0;
				item.y = 0;
				item.width = 50;
				item.height = 50;
				item.degree = 360;
				
				this.drawTag.push(item);
				this.drawCanvas();
			},
			// 点击颜色卡
			onClickColorItem(color){
				if(color){
					this.customColor = color;
					this.canvasBgColor = "#"+color;
					this.drawCanvas();
				}else{
					if(!this.customColor || this.customColor.length < 6){
						this.customColor = "FFFFFF";
						uni.showToast({
							title:"自定义颜色码不合法,请输入6位的十六进制颜色码",
							icon:'none'
						});
						return;
					}else{
						this.canvasBgColor = "#"+this.customColor;
						this.drawCanvas();
					}
				}
			},
			// 绘制的中枢核心方法
			drawCanvas(){
				const ctx = uni.createCanvasContext('canvasId');
			
				// 填充背景色
				ctx.setFillStyle(this.canvasBgColor);
				ctx.fillRect(0, 0, this.canvasSize, this.canvasSize);
				
				// 绘制背景图
				if(this.canvasBG){
					ctx.drawImage(this.canvasBG, this.bgLeft, this.bgTop, this.canvasSize, this.canvasSize);
				}
				
				// 饰品点缀图以及旋转逻辑处理
				this.drawTag.forEach(item=>{
					if(item.url){
						// 旋转的处理代码 
						if(item.degree > 0){
							ctx.translate(item.x + item.width / 2, item.y + item.height / 2);
							ctx.rotate(item.degree * Math.PI / 180);
							ctx.translate((item.x + item.width / 2) * -1, (item.y + item.height / 2) * -1);
						}
						
						if(this.moveElement && item.id === this.moveElement.id){
							ctx.setStrokeStyle('red');
							ctx.strokeRect(item.x - 2 , item.y - 2 , item.width + 2, item.height + 2)
						}
						
						// 绘制饰品点缀图
						//#ifdef MP
						ctx.drawImage(item.url_mp, item.x, item.y, item.width, item.height);
						//#endif
						
						//#ifndef MP
						ctx.drawImage(item.url, item.x, item.y, item.width, item.height);
						//#endif
					}
				});
				
				ctx.draw();
			},
			// 触摸开始,用于定位点击的是哪个tag装饰
			onTouchStart(e){
				let x = e.touches[0].x;
				let y = e.touches[0].y;
				
				x = x < 0 ? 0 : x;
				y = y < 0 ? 0 : y;
				
				x = x > this.canvasSize ? this.canvasSize : x;
				y = y > this.canvasSize ? this.canvasSize : y;
				
				let cur = this.drawTag.find(item=>{
					return ((x >= item.x && y >= item.y) && (x <= (item.x + item.width) && y <= (item.y + item.height)));
				});
				
				if(cur){
					this.moveElement = cur;
				}
				
				this.drawCanvas();
			},
			// 移动进行中,拖动生效
			onTouchMove(e){
				let x = e.touches[0].x;
				let y = e.touches[0].y;
				
				x = x < 0 ? 0 : x;
				y = y < 0 ? 0 : y;
				
				x = x - this.moveElement.width;
				y = y - this.moveElement.height;
				
				// let offset = 20;
				// x = x > this.canvasSize - this.moveElement.width - offset ? this.canvasSize - this.moveElement.width - offset : x;
				// y = y > this.canvasSize - this.moveElement.height - offset ? this.canvasSize - this.moveElement.height - offset: y;
				
				this.moveElement.x = x;
				this.moveElement.y = y;
				
				this.drawCanvas();
			},
		}
	}
</script>

<style>
@import url('index.css');
</style>

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值