<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Uniapp簡易使用canvas繪制分享海報

      使用UniApp Canvas實現分享海報

      一、分享海報

      現在使用 Uniapp 中的 canvas 簡單實現下商品的分享海報,附上二維碼(這個可以附上各種信息例如分享綁定下單等關系),開箱即用。

      • 動態生成包含商品信息、用戶二維碼的分傭海報
      • 一鍵保存到手機相冊
      • 支持App原生分享和小程序分享
      • 打通社交裂變傳播路徑

      注:這里的分享功能用了微信的 showShareImageMenu,會調起朋友分享、朋友圈分享、收藏、保存圖片等,會跟頁面功能重復,并且使用這個接口記得綁定項目的 appid,否則會報錯。


      二、技術支持(使用 Uniapp canvas,直接復制進行更改就行)

      <template>
      	<view class="container">
      		<!-- 商品展示區域 -->
      		<view class="product-canvas">
      			<canvas canvas-id="productCanvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
      				id="productCanvas" class="canvas" />
      			<loading v-if="loading"></loading>
      		</view>
      
      		<!-- 四個功能按鈕 -->
      		<view class="functions">
      			<view class="function-item" @tap="shareToFriend">
      				<view class="icon-circle icon-share"></view>
      				<text class="function-text">發送給朋友</text>
      			</view>
      			<view class="function-item" @tap="shareToMoments">
      				<view class="icon-circle icon-moments"></view>
      				<text class="function-text">分享到朋友圈</text>
      			</view>
      			<view class="function-item" @tap="collectProduct">
      				<view class="icon-circle icon-collect"></view>
      				<text class="function-text">收藏</text>
      			</view>
      			<view class="function-item" @tap="savePoster">
      				<view class="icon-circle icon-save"></view>
      				<text class="function-text">保存圖片</text>
      			</view>
      		</view>
      
      <!-- 		<view class="" @click="close" style="
              position: absolute;
              left: 50%;
              bottom: 50rpx;
              transform: translateX(-50%);
            ">
      			<image src="作為組件底部叉叉" mode="widthFix" style="width: 50rpx; height: auto"></image>
      		</view> -->
      	</view>
      </template>
      
      <script>
      	export default {
      		data() {
      			return {
      				canvasWidth: 355, // px
      				canvasHeight: 425,
      				loading: false,
      				canvasPath: "",
      				product: {
      					name: "海天調味品十件套",
      					desc_text: "精選優質原料,家庭烹飪必備套裝,含醬油、蠔油、陳醋、料酒等多種調味品",
      					market_price: 39.9,
      					pic: "https://dummyimage.com/180x230/f5f5f5/999",
      				},
      				qrcode: "https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=https://shop.example.com",
      			};
      		},
      		onLoad() {
      			this.open()
      		},
      		methods: {
      			open() {
      				this.drawCanvas();
      			},
      			close() {
      				this.$emit("update:show", !this.show);
      			},
      			shareToFriend() {
      				const that = this;
      				// #ifdef APP
      				uni.share({
      					provider: "weixin",
      					scene: "WXSceneSession",
      					type: 2,
      					imageUrl: that.canvasPath,
      					success(res) {
      						console.log("分享給朋友成功", res);
      						uni.showToast({
      							title: "已分享給朋友",
      							icon: "success",
      						});
      					},
      					fail(err) {
      						console.log("分享給朋友失敗", err);
      						uni.showToast({
      							title: "分享失敗,請重試",
      							icon: "none",
      						});
      					},
      				});
      				// #endif
      				// #ifdef MP-WEIXIN
      				uni.showShareImageMenu({
      					path: that.canvasPath,
      					success() {},
      					fail(err) {
      						console.log(err)
      					}
      				});
      				// #endif
      			},
      			shareToMoments() {
      				const that = this;
      				// #ifdef APP
      				uni.share({
      					provider: "weixin",
      					scene: "WXSceneTimeline",
      					type: 2,
      					imageUrl: that.canvasPath,
      					success(res) {
      						console.log("分享到朋友圈成功", res);
      						uni.showToast({
      							title: "已分享到朋友圈",
      							icon: "success",
      						});
      					},
      					fail(err) {
      						console.log("分享到朋友圈失敗", err);
      						uni.showToast({
      							title: "分享失敗,請重試",
      							icon: "none",
      						});
      					},
      				});
      				// #endif
      				// #ifdef MP-WEIXIN
      				wx.showShareImageMenu({
      					path: that.canvasPath,
      					success() {},
      				});
      				// #endif
      			},
      			collectProduct() {
      				// #ifdef MP-WEIXIN
      				const that = this;
      				wx.addFileToFavorites({
      					filePath: that.canvasPath,
      					success: function() {
      						console.log("收藏成功");
      						uni.showToast({
      							title: "收藏成功",
      							icon: "success",
      						});
      					},
      					fail: function(err) {
      						console.error("收藏失敗:", err);
      						uni.showToast({
      							title: "收藏失敗",
      							icon: "error",
      						});
      					},
      				});
      				// #endif
      			},
      			savePoster() {
      				const that = this;
      				uni.saveImageToPhotosAlbum({
      					filePath: that.canvasPath,
      					success(res) {
      						uni.showToast({
      							title: "保存成功",
      							icon: "success",
      						});
      					},
      				});
      			},
      			async drawCanvas() {
      				this.loading = true;
      				const that = this;
      				const dpr = uni.getSystemInfoSync().pixelRatio;
      				const width = this.canvasWidth;
      				const height = this.canvasHeight;
      				const ctx = uni.createCanvasContext("productCanvas", this);
      				// ctx.canvas.width = width * dpr;
      				// ctx.canvas.height = height * dpr;
      				// ctx.scale(dpr, dpr);
      				const {
      					pic: image,
      					name: title,
      					desc_text: desc,
      					market_price: price,
      				} = this.product;
      				const qrcode = this.qrcode;
      				// 背景白色 + 紅色邊框
      				const borderMargin = 20;
      				const borderWidth = 3;
      				ctx.setFillStyle("#fff");
      				ctx.fillRect(0, 0, width, height);
      				ctx.setStrokeStyle("#e60012");
      				ctx.setLineWidth(borderWidth);
      				// 邊框內縮繪制
      				ctx.strokeRect(
      					borderMargin + borderWidth / 2,
      					borderMargin + borderWidth / 2,
      					this.canvasWidth - 2 * (borderMargin + borderWidth / 2),
      					this.canvasHeight - 2 * (borderMargin + borderWidth / 2)
      				);
      
      				// 徽章
      				const badgeX = 190;
      				const badgeY = 5;
      				const badgeW = 125;
      				const badgeH = 30;
      				const badgeRadius = 15;
      
      				// 陰影模擬(底層填充深色模糊)
      				ctx.setFillStyle("rgba(230, 0, 18, 0.1)");
      				ctx.beginPath();
      				ctx.moveTo(badgeX + badgeRadius, badgeY + 4);
      				ctx.arcTo(
      					badgeX + badgeW,
      					badgeY + 4,
      					badgeX + badgeW,
      					badgeY + badgeH + 4,
      					badgeRadius
      				);
      				ctx.arcTo(
      					badgeX + badgeW,
      					badgeY + badgeH + 4,
      					badgeX,
      					badgeY + badgeH + 4,
      					badgeRadius
      				);
      				ctx.arcTo(badgeX, badgeY + badgeH + 4, badgeX, badgeY + 4, badgeRadius);
      				ctx.arcTo(badgeX, badgeY + 4, badgeX + badgeW, badgeY + 4, badgeRadius);
      				ctx.closePath();
      				ctx.fill();
      
      				// 繪制漸變圓角背景
      				const gradient = ctx.createLinearGradient(badgeX, 0, badgeX + badgeW, 0);
      				gradient.addColorStop(0, "#ff4d6d");
      				gradient.addColorStop(1, "#e60012");
      
      				ctx.setFillStyle(gradient);
      				ctx.beginPath();
      				ctx.moveTo(badgeX + badgeRadius, badgeY);
      				ctx.arcTo(
      					badgeX + badgeW,
      					badgeY,
      					badgeX + badgeW,
      					badgeY + badgeH,
      					badgeRadius
      				);
      				ctx.arcTo(
      					badgeX + badgeW,
      					badgeY + badgeH,
      					badgeX,
      					badgeY + badgeH,
      					badgeRadius
      				);
      				ctx.arcTo(badgeX, badgeY + badgeH, badgeX, badgeY, badgeRadius);
      				ctx.arcTo(badgeX, badgeY, badgeX + badgeW, badgeY, badgeRadius);
      				ctx.closePath();
      				ctx.fill();
      
      				// 白色文字
      				ctx.setFontSize(14);
      				ctx.setFillStyle("#fff");
      				ctx.setTextAlign("center");
      				ctx.setTextBaseline("middle");
      				ctx.fillText("分享海報", badgeX + badgeW / 2, badgeY + badgeH / 2);
      
      				// 商品圖
      				await this.drawImage(ctx, image, 40, 50, 120, 150);
      
      				// 標題
      				ctx.setFontSize(18);
      				ctx.setFillStyle("#333");
      				ctx.setTextAlign("left");
      				ctx.font = "bold 18px sans-serif";
      				const titleLines = this.splitText(title, 160, ctx);
      				titleLines.forEach((line, index) => {
      					ctx.fillText(line, 170, 60 + index * 20);
      				});
      
      				// 描述(多行)
      				ctx.setFontSize(14);
      				ctx.setFillStyle("#666");
      				const lines = this.splitText(desc, 160, ctx);
      				lines.forEach((line, index) => {
      					ctx.fillText(line, 170, 85 + titleLines.length * 20 + index * 18);
      				});
      
      				// 價格
      				ctx.setFontSize(20);
      				ctx.setFillStyle("#e60012");
      				ctx.fillText(
      					"¥" + price.toFixed(2),
      					170,
      					110 + titleLines.length * 20 + lines.length * 18
      				);
      
      				// 小店名
      				ctx.setFontSize(16);
      				ctx.setFillStyle("#07c160");
      				ctx.fillText(
      					"微信小店",
      					50,
      					200 + titleLines.length * 20 + lines.length * 18
      				);
      
      				// 提示
      				ctx.setFontSize(12);
      				ctx.setFillStyle("#999");
      				ctx.fillText(
      					"微信掃一掃購買",
      					45,
      					230 + titleLines.length * 20 + lines.length * 18
      				);
      
      				// 二維碼
      				await this.drawImage(
      					ctx,
      					qrcode,
      					200,
      					160 + titleLines.length * 20 + lines.length * 18,
      					90,
      					90
      				);
      
      				ctx.draw(true, () => {
      					setTimeout(() => {
      						uni.canvasToTempFilePath({
      								destWidth: that.canvasWidth,
      								destHeight: that.canvasHeight,
      								canvasId: "productCanvas",
      								success: (res) => {
      									console.log("臨時圖片路徑:", res.tempFilePath);
      									that.canvasPath = res.tempFilePath;
      								},
      							},
      							that
      						);
      					}, 100);
      				});
      			},
      
      			// 遠程圖片繪制
      			drawImage(ctx, src, x, y, w, h) {
      				return new Promise((resolve) => {
      					uni.getImageInfo({
      						src,
      						success: (res) => {
      							ctx.drawImage(res.path, x, y, w, h);
      							this.loading = false;
      							resolve();
      						},
      						fail: () => {
      							console.warn("圖片加載失敗", src);
      							this.loading = false;
      							resolve();
      						},
      					});
      				});
      			},
      
      			// 文本換行
      			splitText(text, maxWidth, ctx) {
      				const result = [];
      				let temp = "";
      				for (let char of text) {
      					const testLine = temp + char;
      					const {
      						width
      					} = ctx.measureText(testLine);
      					if (width > maxWidth) {
      						result.push(temp);
      						temp = char;
      					} else {
      						temp = testLine;
      					}
      				}
      				if (temp) result.push(temp);
      				return result;
      			},
      		},
      	};
      </script>
      <style scoped>
      	.container {
      		height: 100vh;
      		width: 100%;
      		display: flex;
      		flex-direction: column;
      		justify-content: space-between;
      	}
      
      	/* 商品展示區域 - Canvas */
      	.product-canvas {
      		width: 100%;
      		background: linear-gradient(135deg, #fff8f8, #fff);
      		position: relative;
      		display: flex;
      		flex-direction: column;
      		justify-content: center;
      		align-items: center;
      		padding: 50rpx 0;
      	}
      
      	.canvas {
      		width: 315px;
      		height: 425px;
      	}
      
      	.canvas-content {
      		width: 90%;
      		height: 85%;
      		background: #fff;
      		border: 3px solid #e60012;
      		border-radius: 12px;
      		box-shadow: 0 8px 20px rgba(230, 0, 18, 0.1);
      		padding: 20rpx;
      		position: relative;
      	}
      
      	.badge {
      		position: absolute;
      		top: -16px;
      		right: 20px;
      		background: linear-gradient(to right, #ff4d6d, #e60012);
      		color: white;
      		padding: 8px 16px;
      		border-radius: 20px;
      		font-weight: bold;
      		font-size: 14px;
      		box-shadow: 0 4px 10px rgba(230, 0, 18, 0.3);
      	}
      
      	.product-info {
      		display: flex;
      		height: 100%;
      	}
      
      	.product-image {
      		flex: 1;
      		background: #f9f9f9;
      		border-radius: 8px;
      		display: flex;
      		justify-content: center;
      		align-items: center;
      		overflow: hidden;
      	}
      
      	.product-image img {
      		width: 100%;
      		height: 100%;
      		object-fit: contain;
      	}
      
      	.product-details {
      		flex: 1;
      		padding: 20rpx;
      		display: flex;
      		flex-direction: column;
      		justify-content: space-between;
      	}
      
      	.product-title {
      		font-size: 32rpx;
      		font-weight: bold;
      		color: #333;
      		margin-bottom: 10px;
      	}
      
      	.product-desc {
      		font-size: 14px;
      		color: #666;
      		line-height: 1.5;
      	}
      
      	.price {
      		margin: 15px 0;
      		color: #e60012;
      		font-weight: bold;
      		font-size: 38rpx;
      	}
      
      	.qrcode-section {
      		display: flex;
      		align-items: center;
      		justify-content: space-between;
      		margin-top: 20px;
      		padding-top: 15px;
      		border-top: 1px dashed #eee;
      	}
      
      	.qrcode {
      		width: 100px;
      		height: 100px;
      		background: #f5f5f5;
      		display: flex;
      		justify-content: center;
      		align-items: center;
      		margin-bottom: 8px;
      	}
      
      	.qrcode-hint {
      		color: #999;
      		font-size: 12px;
      	}
      
      	.wx-store {
      		color: #07c160;
      		font-weight: bold;
      		font-size: 15px;
      		margin-top: 5px;
      	}
      
      	/* 功能區樣式 */
      	.functions {
      		display: flex;
      		flex-direction: row;
      		background-color: #fff;
      		padding: 25px 10px;
      		border-top: 1px solid #f0f0f0;
      	}
      
      	.function-item {
      		flex: 1;
      		display: flex;
      		flex-direction: column;
      		align-items: center;
      		padding: 10px 0;
      		transition: all 0.3s;
      	}
      
      	.function-item:active {
      		background-color: #f9f9f9;
      		transform: translateY(2px);
      	}
      
      	.icon-circle {
      		width: 70rpx;
      		height: 70rpx;
      		border-radius: 50%;
      		display: flex;
      		justify-content: center;
      		align-items: center;
      		margin-bottom: 12rpx;
      	}
      
      	.icon-share {
      		background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
      	}
      
      	.icon-moments {
      		background: linear-gradient(135deg, #3ae7b1 0%, #00d2a9 100%);
      	}
      
      	.icon-collect {
      		background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%);
      	}
      
      	.icon-save {
      		background: linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%);
      	}
      
      	.function-text {
      		font-size: 26rpx;
      		color: #555;
      	}
      </style>
      

      三、性能優化與注意事項

      1. 使用問題

      • Canvas 問題:這里的 canvas 寬高使用固定的px格式,這里沒做過多的適配,需要各位自己進行適配,并且繪制的時候 canvas 的背景設置的是白色,因為要作為圖片進行保存,如果對其分裝為組件的時候要注意層級關系并且白色背景跟 mask 背景和組件背景要做好適配兼容。還有 canvas 頂部的徽章在真機可能沒有那么好看,自己再進行優化吧。
      • 模糊問題:使用pixelRatio適配高分屏,上面沒做,注釋了
      • 文字溢出:這里的文字有做分割,如果過長可能還需進行優化

      2. 性能優化建議

      1. 預加載網絡圖片
      2. 對繪制操作進行節流控制
      3. 使用離屏Canvas處理復雜圖形

      四、效果展示

      商品分傭海報示例
      商品分傭海報示例


      五、擴展思路

      1. 動態模板:配置不同風格的海報模板
      2. 海報審核:對接內容安全API
      3. 數據分析:跟蹤海報分享轉化率
      4. 裂變激勵:如有是有自己的一些模式的話,可以分享后給予傭金獎勵
      posted @ 2025-07-06 21:36  幼兒園技術家  閱讀(591)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品夜夜春夜夜爽久久小| 青青草原网站在线观看| 抚远县| 亚洲十八禁一区二区三区| 亚洲嫩模喷白浆在线观看| 国产精品va无码一区二区| 亚洲最大福利视频网| 亚洲av色香蕉一区二区| 4hu四虎永久在线观看| 国产av国片精品一区二区| 人人入人人爱| 国产农村妇女高潮大叫| 磐安县| 狠狠色噜噜狠狠狠狠av不卡| 国产精品自在自线视频| 丰满熟妇人妻av无码区| 中文字幕久久六月色综合| 99久久久国产精品免费无卡顿| 色综合久久综合欧美综合网| 亚洲AV成人片不卡无码| 鲁丝片一区二区三区免费| 性xxxx欧美老妇胖老太性多毛 | 久久综合久中文字幕青草| 化隆| 国产精品污双胞胎在线观看| 加勒比中文字幕无码一区| 一区二区三区鲁丝不卡| 日韩精品无码一区二区三区视频| 熟女系列丰满熟妇AV| 商水县| 一本色道婷婷久久欧美| 国产精品推荐手机在线| 亚洲国产成人综合精品| 人成午夜免费视频无码| 狠狠人妻久久久久久综合九色| 国产精品一区二区中文| 天天噜噜日日久久综合网| 大桥未久亚洲无av码在线| 美女把尿囗扒开让男人添| 亚洲成女人图区一区二区| AV最新高清无码专区|