uniapp使用Canvas生成電子名片
工作中有生成電子名片的一個需求,剛剛好弄了發一下分享分享
文章目錄
- uniapp使用Canvas生成電子名片
- 前言
- 一、上代碼?
- 總結
前言
先看效果
一、上代碼?
不對不對應該是上才藝,哈哈哈
<template><view class="container"><view class="head" style="height: 7rem;"><view style="color:#fff;position: fixed;top: 4rem;left: 1rem;"><br><text style="font-size: 11px;"></text></view></view><view style="width: 100%; height: 7.5rem;"></view><scroll-view scroll-y style="height: 100vh;" class="content"><view style="padding-top: 30rpx;"><!-- 立體名片卡片 --><view class="card-container" id="cardContainer"><view class="business-card"><!-- 頂部彩色邊緣標簽 --><view class="card-edge"></view><view class="card-content"><view class="card-title">褲架工廠</view><view class="card-subtitle">專做軟體沙發</view><view class="card-details"><view class="detail-item"><view>高級產品設計師</view></view><view class="detail-item"><view>創新事業部 | ABC科技</view></view><view class="detail-item"><view>工廠地址:上海浦東新區三林東地鐵左邊大廈23</view></view></view></view><!-- 雙二維碼容器 --><view class="qrcode-column"><view class="qrcode-item"><view class="card-image-container"><image class="card-image"src="https://linda-uni.oss-cn-guangzhou.aliyuncs.com/images/and/app_20230925_20250529092745.png"mode="aspectFit"></image></view><view class="qrcode-label">微信聯系</view></view><view class="qrcode-item"><view class="card-image-container"><image class="card-image"src="https://linda-uni.oss-cn-guangzhou.aliyuncs.com/images/and/app_20230925_20250529092745.png"mode="aspectFit"></image></view><view class="qrcode-label">官方網站</view></view></view><!-- 角落裝飾 --><view class="card-corner corner-tl"></view><view class="card-corner corner-br"></view></view></view><view class="save-btn" @click="saveCardImage"><text>保存名片圖片</text></view></view></scroll-view><canvas canvas-id="cardCanvas":style="{ position: 'absolute', left: '-9999px', width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas></view></template><script>export default {data() {return {canvasWidth: 1100,canvasHeight: 630,dpr: 1,qrcode1: 'https://linda-uni.oss-cn-guangzhou.aliyuncs.com/images/and/app_20230925_20250529092745.png',qrcode2: 'https://linda-uni.oss-cn-guangzhou.aliyuncs.com/images/and/app_20230925_20250529092745.png',backgroundImage: 'https://linda-uni.oss-cn-guangzhou.aliyuncs.com/images/and/550e8a1a-e450-4880-9441-33608476b32a.jpg'}},mounted() {},methods: {// 保存名片為圖片 async saveCardImage() {uni.showLoading({title: '生成中...',mask: true});try {// 1. 繪制Canvasawait this.drawCardCanvas();// 2. 生成臨時圖片路徑const tempPath = await new Promise((resolve, reject) => {uni.canvasToTempFilePath({canvasId: 'cardCanvas',quality: 1,fileType: 'png',width: this.canvasWidth * this.dpr,height: this.canvasHeight * this.dpr,destWidth: this.canvasWidth * this.dpr,destHeight: this.canvasHeight * this.dpr,success: res => resolve(res.tempFilePath),fail: err => reject(err)});});// 3. 保存到相冊await new Promise((resolve, reject) => {uni.saveImageToPhotosAlbum({filePath: tempPath,success: () => resolve(),fail: err => reject(err)});});uni.hideLoading();uni.showToast({title: '保存成功',icon: 'success'});} catch (err) {uni.hideLoading();uni.showToast({title: '保存失敗: ' + (err.errMsg || err),icon: 'none'});console.error('保存失敗:', err);}},// 繪制名片到Canvasasync drawCardCanvas() {const ctx = uni.createCanvasContext('cardCanvas', this);const dpr = this.dpr;const width = this.canvasWidth;const height = this.canvasHeight;const padding = 30 * dpr;// 清除畫布ctx.clearRect(0, 0, width, height);// 1. 繪制背景// ctx.setFillStyle('#1F1F1F');// ctx.fillRect(0, 0, width, height);// 2. 繪制名片背景const cardWidth = width - padding * 2;const cardHeight = height - padding * 2;// 繪制名片背景if (this.backgroundImage) {// 使用圖片背景try {await this.drawRoundedImage(ctx,this.backgroundImage,padding,padding,cardWidth,cardHeight,20 * dpr);} catch (e) {console.error('背景圖片加載失敗:', e);// 回退到默認背景this.drawDefaultBackground(ctx, padding, cardWidth, cardHeight);}} else {// 使用顏色背景this.drawDefaultBackground(ctx, padding, cardWidth, cardHeight);}// // 漸變背景// const gradient = ctx.createLinearGradient(0, 0, cardWidth, cardHeight); // gradient.addColorStop(0, '#302f30');// gradient.addColorStop(1, '#282728');// ctx.setFillStyle(gradient);// // 圓角矩形// this.drawRoundedRect(ctx, padding, padding, cardWidth, cardHeight, 20 * dpr);// ctx.fill();// 3. 頂部彩色邊緣(帶圓角)const topHeight = 16 * dpr; // 邊緣高度const topRadius = 20 * dpr; // 圓角半徑// 創建漸變const edgeGradient = ctx.createLinearGradient(padding, padding, padding + cardWidth, padding);edgeGradient.addColorStop(0, '#ff9a9e');edgeGradient.addColorStop(0.3, '#fad0c4');edgeGradient.addColorStop(0.6, '#a18cd1');edgeGradient.addColorStop(1, '#fbc2eb');ctx.setFillStyle(edgeGradient);// 繪制帶圓角的頂部邊緣(僅左上和右上圓角)ctx.beginPath();// 從左上圓角結束點開始ctx.moveTo(padding + topRadius, padding);// 繪制上邊緣線ctx.lineTo(padding + cardWidth - topRadius, padding);// 繪制右上圓角ctx.arcTo(padding + cardWidth, padding,padding + cardWidth, padding + topHeight,topRadius);// 繪制右邊緣ctx.lineTo(padding + cardWidth, padding + topHeight);// 繪制下邊緣(向左)ctx.lineTo(padding, padding + topHeight);// 繪制左邊緣(向上)ctx.lineTo(padding, padding + topRadius);// 繪制左上圓角ctx.arcTo(padding, padding,padding + topRadius, padding,topRadius);ctx.closePath();ctx.fill();// 4. 設置字體 ctx.setFontSize(40 * dpr);ctx.setFillStyle('#f0f0f0');ctx.fillText('褲架工廠', padding + 40 * dpr, padding + 80 * dpr);ctx.setFontSize(28 * dpr);ctx.setFillStyle('#CCCCCC');ctx.fillText('專做軟體沙發', padding + 40 * dpr, padding + 130 * dpr);// 繪制下劃線ctx.setStrokeStyle('#ff9a9e');ctx.setLineWidth(6 * dpr);ctx.beginPath();ctx.moveTo(padding + 40 * dpr, padding + 90 * dpr);ctx.lineTo(padding + 120 * dpr, padding + 90 * dpr);ctx.stroke();// 5. 繪制詳情信息const details = ['高級產品設計師','創新事業部 | ABC科技','工廠地址:上海浦東新區三林東地鐵左邊大廈23'];ctx.setFontSize(28 * dpr);ctx.setFillStyle('#b0b0b0');details.forEach((text, i) => {ctx.fillText(text, padding + 40 * dpr, padding + 200 * dpr + i * 50 * dpr);});// 6. 繪制二維碼 (修改部分開始)const qrSize = cardWidth * 0.12; // 縮小二維碼尺寸const qrY = padding + 350 * dpr;const qrSpacing = 30 * dpr; // 二維碼間距const qrRadius = 20 * dpr; // 圓角半徑const wxX = padding + 40 * dpr;await this.drawRoundedImage(ctx,this.qrcode1,wxX,qrY,qrSize,qrSize,qrRadius);// 官網二維碼 (靠左,在微信二維碼右側)const webX = wxX + qrSize + qrSpacing;await this.drawRoundedImage(ctx,this.qrcode2,webX,qrY,qrSize,qrSize,qrRadius);ctx.setFontSize(24 * dpr);ctx.setFillStyle('#e0e0e0');// 微信文字居中ctx.setTextAlign('center');ctx.fillText('客戶群', wxX + qrSize / 2, qrY + qrSize + 30 * dpr);// 官網文字居中ctx.setTextAlign('center');ctx.fillText('國內站', webX + qrSize / 2, qrY + qrSize + 30 * dpr);// 恢復對齊方式ctx.setTextAlign('left');// 執行繪制ctx.draw();// 返回繪制完成的Promisereturn new Promise(resolve => {setTimeout(resolve, 500);});},// 新增方法:繪制圓角圖片async drawRoundedImage(ctx, src, x, y, width, height, radius) {return new Promise((resolve, reject) => {uni.getImageInfo({src: src,success: (res) => {// 保存當前狀態ctx.save();// 創建圓角矩形路徑 this.drawRoundedRect(ctx, x, y, width, height, radius);ctx.clip(); // 裁剪為圓角矩形// 繪制圖片ctx.drawImage(res.path, x, y, width, height);// 恢復狀態ctx.restore();resolve();},fail: reject});});},// 工具方法:繪制圓角矩形drawRoundedRect(ctx, x, y, width, height, radius) {ctx.beginPath();ctx.moveTo(x + radius, y);ctx.lineTo(x + width - radius, y);ctx.arcTo(x + width, y, x + width, y + radius, radius);ctx.lineTo(x + width, y + height - radius);ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);ctx.lineTo(x + radius, y + height);ctx.arcTo(x, y + height, x, y + height - radius, radius);ctx.lineTo(x, y + radius);ctx.arcTo(x, y, x + radius, y, radius);ctx.closePath();},drawDefaultBackground(ctx, padding, cardWidth, cardHeight) {const gradient = ctx.createLinearGradient(0, 0, cardWidth, cardHeight);gradient.addColorStop(0, '#302f30');gradient.addColorStop(1, '#282728');ctx.setFillStyle(gradient);this.drawRoundedRect(ctx, padding, padding, cardWidth, cardHeight, 20);ctx.fill();},// 工具方法:繪制裝飾角drawCorner(ctx, x, y, size, position) {ctx.setStrokeStyle('#ff9a9e');ctx.setLineWidth(3 * this.dpr);ctx.beginPath();// 圓角半徑const radius = 12 * this.dpr;if (position === 'tl') {// 左上角 - 包圍式圓角ctx.moveTo(x - size, y);ctx.lineTo(x, y);ctx.lineTo(x, y - size);// 添加圓角效果ctx.moveTo(x, y - radius);ctx.arc(x + radius, y - radius, radius, Math.PI, 1.5 * Math.PI);} else if (position === 'br') {// 右下角 - 包圍式圓角ctx.moveTo(x + size, y);ctx.lineTo(x, y);ctx.lineTo(x, y + size);// 添加圓角效果ctx.moveTo(x, y + radius);ctx.arc(x - radius, y + radius, radius, 0.5 * Math.PI, Math.PI);}ctx.stroke();},// 工具方法:繪制圖片(支持網絡圖片)drawImage(ctx, src, x, y, width, height) {return new Promise((resolve, reject) => {uni.getImageInfo({src: src,success: res => {ctx.drawImage(res.path, x, y, width, height);resolve();},fail: (err) => {console.error('圖片加載失敗:', err);reject(err);}});});}},}</script><style>page {background-color: #1F1F1F;}.container {position: relative;height: 100vh;width: 100vw;overflow: hidden;}.head {width: 100%;position: fixed;top: 0px;left: 0%;z-index: 99999;}/* 新增樣式 */.card-container {position: relative;width: 88%;margin: 0 auto 40rpx;perspective: 1000px;}.business-card {background: linear-gradient(145deg, #302f30, #282728);border-radius: 20rpx;padding: 30rpx;/* display: flex; */justify-content: space-between;box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.4);position: relative;overflow: hidden;transform: translateY(0);transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);}.business-card:active {transform: translateY(-10rpx);box-shadow: 0 15rpx 40rpx rgba(0, 0, 0, 0.6);}/* 邊緣裝飾效果 */.card-edge {position: absolute;top: 0;left: 0;width: 100%;height: 16rpx;background: linear-gradient(90deg, #ff9a9e, #fad0c4, #a18cd1, #fbc2eb);border-radius: 20rpx 20rpx 0 0;}.card-corner {position: absolute;width: 40rpx;height: 40rpx;border: 3rpx solid #ff9a9e;}.corner-tl {top: 20rpx;left: 20rpx;border-right: none;border-bottom: none;border-top-left-radius: 12rpx;}.corner-br {bottom: 20rpx;right: 20rpx;border-left: none;border-top: none;border-bottom-right-radius: 12rpx;}.card-content {flex: 1;padding-right: 20rpx;}.card-title {font-size: 40rpx;font-weight: bold;color: #f0f0f0;margin-bottom: 16rpx;position: relative;display: inline-block;}.card-title::after {content: '';position: absolute;bottom: -8rpx;left: 0;width: 80rpx;height: 6rpx;background: linear-gradient(90deg, #ff9a9e, #fad0c4);border-radius: 4rpx;}.card-subtitle {font-size: 28rpx;color: #CCCCCC;margin: 30rpx 0 20rpx;line-height: 1.5;}.card-details {display: flex;flex-direction: column;gap: 20rpx;margin-top: 30rpx;}.detail-item {display: flex;align-items: center;font-size: 28rpx;color: #b0b0b0;}.detail-icon {width: 50rpx;color: #ff9a9e;font-size: 32rpx;margin-right: 15rpx;}.card-image-container {flex-shrink: 0;width: 180rpx;height: 180rpx;border-radius: 15rpx;overflow: hidden;box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.3);border: 2rpx solid rgba(255, 255, 255, 0.1);position: relative;background: #fff;display: flex;align-items: center;justify-content: center;transition: all 0.3s ease;}.card-image-container:active {transform: scale(1.05);box-shadow: 0 12rpx 25rpx rgba(0, 0, 0, 0.5);}.card-image {width: 90%;height: 90%;display: block;}/* 雙二維碼樣式 */.qrcode-column {display: flex;/* flex-direction: column; *//* align-items: center; *//* justify-content: center; */flex-shrink: 0;width: 100%;gap: 25rpx;margin-top: 20rpx;}.qrcode-item {display: flex;flex-direction: column;align-items: center;}.qrcode-label {color: #e0e0e0;font-size: 24rpx;text-align: center;margin-top: 10rpx;font-weight: 500;}.save-btn {margin: 40rpx auto;padding: 20rpx 40rpx;background: linear-gradient(90deg, #ff9a9e, #a18cd1);color: white;border-radius: 50rpx;width: 60%;text-align: center;font-weight: bold;box-shadow: 0 8rpx 20rpx rgba(161, 140, 209, 0.4);}</style>
總結
這里面就js 里面的有用,其他的我也沒有刪除,要用的可以自己刪除就可以了,go,卷起來