效果图
注意!!!以下方法自行修改:
1.腾讯地图key
2.天气api
3.图片地址
4.js弹窗方法
template
下方的image图片自行寻找替换!
<template>
<view>
<camera
v-if="!tempImagePath && cameraHeight !== 0"
:resolution="'high'"
:frame-size="'large'"
:device-position="device"
:flash="flash"
:style="{
position: 'fixed',
top: '0',
width: cameraWidth + 'px',
height: cameraHeight + 'px',
}"
></camera>
<image
v-else
:src="tempImagePath"
mode="widthFix"
style="width: 100%; position: absolute"
/>
<!-- 预览 -->
<canvas
type="2d"
id="canvasView"
:style="{
width: cameraWidth + 'px',
height: cameraHeight + 'px',
}"
></canvas>
<view class="handle" id="myContainer">
<button class="handle_card" @click="chooseLocaClick">
<image
class="handle_card_icon"
:src="`${imgUrl}/watermark/watermark-loca.png`"
mode="widthFix"
/>
<view class="handle_card_name">定位</view>
</button>
<button class="handle_card" @click="setDevice">
<image
class="handle_card_icon"
:src="`${imgUrl}/watermark/watermark-turn.png`"
mode="widthFix"
/>
<view class="handle_card_name">切换</view>
</button>
<button class="handle_ps" @click="takePhoto">
<image
class="handle_ps_image"
:src="`${imgUrl}/watermark/watermark-shoot.png`"
mode="widthFix"
/>
<view class="handle_ps_name">拍摄</view>
</button>
<button class="handle_card" @click="setFlash">
<image
class="handle_card_icon"
:src="`${imgUrl}/watermark/watermark-flash.png`"
mode="widthFix"
/>
<view class="handle_card_name">闪光</view>
</button>
<button class="handle_card" open-type="share">
<image
class="handle_card_icon"
:src="`${imgUrl}/watermark/watermark-share.png`"
mode="widthFix"
/>
<view class="handle_card_name">分享</view>
</button>
</view>
</view>
</template>
js
开发者秘钥key自行填写,用于定位位置功能!
showToast和showLoading为自行封装的弹窗
如果没有直接用官网的uni.showToast
<script>
const mapSDK = new QQMapWX({
key: "", //申请的开发者秘钥key
});
import { showToast, showLoading } from "@/js/common.js";
export default {
data() {
return {
device: "back",
flash: "",
date: "",
time: "",
week: "",
address: "",
addressName: "",
cameraWidth: 0,
cameraHeight: 0,
canvasWidth: 0,
canvasHeight: 0,
tempImagePath: "",
timer: null,
// 文本是否换行
isWrap: false,
};
},
created() {
const systemInfo = uni.getSystemInfoSync();
const screenWidth = systemInfo.screenWidth;
const screenHeight = systemInfo.screenHeight;
const statusBarHeight = systemInfo.statusBarHeight;
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
const cameraWidth = screenWidth;
uni
.createSelectorQuery()
.select("#myContainer")
.boundingClientRect((rect) => {
const cameraHeight =
screenHeight -
statusBarHeight -
menuButtonInfo.height -
(menuButtonInfo.top - systemInfo.statusBarHeight) * 2 -
rect.height;
this.cameraWidth = cameraWidth;
this.cameraHeight = cameraHeight;
this.getTime();
this.getLocation();
})
.exec();
},
methods: {
// 获取天气
getAnonInfo() {
anonWeatherInfoApi({
city: this.ad_info.adcode,
}).then((res) => {
if (res.status === 200) {
this.cityObj = res.data;
// 获取完天气后初始化画图
this.drawCanvas("#canvasView", "init");
}
});
},
// 画图
drawCanvas(canvasId, type = "init") {
const _this = this;
return new Promise((resolve) => {
if (type == "init") {
const query = uni.createSelectorQuery();
query
.select(canvasId)
.fields({
node: true,
})
.exec((res) => {
ctxFn(res[0].node);
});
} else {
// 创建离屏 2D canvas 实例
const node = uni.createOffscreenCanvas({
type: "2d",
});
ctxFn(node);
}
function ctxFn(node) {
const canvas = node;
const ctx = canvas.getContext("2d");
let camerW = null;
let camerH = null;
if (type === "init") {
camerW = _this.cameraWidth;
camerH = _this.cameraHeight;
const drp = uni.getWindowInfo().pixelRatio;
canvas.width = camerW * drp;
canvas.height = camerH * drp;
ctx.scale(drp, drp);
draw();
} else {
camerW = _this.canvasWidth;
camerH = _this.canvasHeight;
canvas.width = camerW;
canvas.height = camerH;
const image = canvas.createImage();
image.src = _this.tempImagePath;
image.onload = () => {
ctx.drawImage(image, 0, 0);
draw();
};
}
function draw() {
const sizeX = camerW / 375;
// 判断地址是否超出边界
if (_this.isWrap) {
camerH -= 15 * sizeX;
}
ctx.font = `${35 * sizeX}px 黑体`;
ctx.fillStyle = "#ffffff";
ctx.textBaseline = "bottom";
// 绘制时间
ctx.fillText(_this.time, 10 * sizeX, camerH - 30 * sizeX);
const timeWidth = ctx.measureText(_this.time).width;
// 绘制边框线条
ctx.beginPath();
ctx.lineCap = "round";
ctx.moveTo(timeWidth + 16 * sizeX, camerH - 59 * sizeX);
ctx.lineTo(timeWidth + 16 * sizeX, camerH - 36 * sizeX);
ctx.lineWidth = 1.7 * sizeX;
ctx.strokeStyle = "#A9A9A8";
ctx.stroke();
// 绘制年月日
ctx.font = `${12 * sizeX}px 黑体`;
ctx.fillText(
_this.date,
timeWidth + 22 * sizeX,
camerH - 49 * sizeX
);
// 绘制周几
ctx.fillText(
_this.week,
timeWidth + 22 * sizeX,
camerH - 34 * sizeX
);
// 绘制天气
const city = `${_this.cityObj.weather}${_this.cityObj.temperature}°C`;
ctx.fillText(city, timeWidth + 60 * sizeX, camerH - 34 * sizeX);
// 绘制换行地址
ctx.font = `bold ${14 * sizeX}px 黑体`;
_this.drawText(
ctx,
_this.address,
10 * sizeX,
camerH - 10 * sizeX,
camerW
);
if (type !== "init") {
const imgData = canvas.toDataURL();
const time = new Date().getTime();
const fs = uni.getFileSystemManager();
const filePath =
uni.env.USER_DATA_PATH + "/poster" + time + "share" + ".png";
fs.writeFile({
filePath,
data: imgData.replace(/^data:image\/\w+;base64,/, ""),
encoding: "base64",
success: () => {
resolve(filePath);
},
});
}
}
}
});
},
// 获取并更新时间的方法
getTime() {
this.timeSet();
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.timer = setInterval(() => {
this.timeSet();
this.drawCanvas("#canvasView", "init");
}, 1000);
},
// 时间更新
timeSet() {
const timeData = this.formatTime();
this.date = timeData.date;
this.time = timeData.time;
this.week = timeData.week;
},
// 获取并更新位置信息的方法
getLocation() {
uni.getLocation({
isHighAccuracy: true,
type: "gcj02",
success: (Locares) => {
locationToAddress(Locares).then((res) => {
this.address = `${res.address_component.city}${res.formatted_addresses.recommend}`;
// 获取天气
this.ad_info = res.ad_info;
this.getAnonInfo();
});
},
});
},
// 绘制换行文本
drawText(ctx, str, leftWidth, initHeight, canvasWidth) {
let lineWidth = 0;
let lastSubStrIndex = 0;
this.isWrap = false;
for (let i = 0; i < str.length; i++) {
lineWidth += ctx.measureText(str[i]).width;
if (lineWidth > canvasWidth - 20) {
this.isWrap = true;
ctx.fillText(str.slice(lastSubStrIndex, i), leftWidth, initHeight);
initHeight += (15 * canvasWidth) / 375;
lineWidth = 0;
lastSubStrIndex = i;
}
}
ctx.fillText(str.slice(lastSubStrIndex), leftWidth, initHeight);
},
// 拍摄事件
takePhoto() {
const ctx = uni.createCameraContext();
ctx.takePhoto({
quality: "normal",
success: (res) => {
showLoading("图片生成中...");
// 兼容横屏旋转,如果宽度大于高度,旋转90度,否则不旋转
const isEdg = res.width > res.height;
rotateAndCropImage(res.tempImagePath, isEdg ? 90 : 0, (rotateRes) => {
this.tempImagePath = rotateRes.tempFilePath;
const edgImgRes = isEdg ? rotateRes : res;
this.canvasWidth = edgImgRes.width;
this.canvasHeight = edgImgRes.height;
this.drawCanvas("#canvasView", "draw").then((imgPath) => {
uni.hideLoading();
uni.saveImageToPhotosAlbum({
filePath: imgPath,
complete: (e) => {
showToast(
e.errMsg.includes("fail cancel") ? "保存失败" : "保存成功"
);
this.tempImagePath = "";
// 删除临时文件目录图片
deleteWxFile();
},
});
});
});
},
});
},
// 切换摄像头
setDevice() {
this.device = this.device === "back" ? "front" : "back";
const text = this.device === "back" ? "后置" : "前置";
showToast(`摄像头${text}`);
},
// 闪光灯开关
setFlash() {
this.flash = this.flash === "torch" ? "off" : "torch";
},
// 选择位置信息
chooseLocaClick() {
uni.chooseLocation({
success: (res) => {
locationToAddress(res).then((loca) => {
this.address = `${loca.address_component.city}${loca.formatted_addresses.recommend}`;
// 选择完位置更新画图
this.drawCanvas("#canvasView", "init");
});
},
});
},
// 时间转换
formatTime() {
const date = new Date();
const [year, month, day, hour, minute] = [
date.getFullYear(),
String(date.getMonth() + 1).padStart(2, "0"),
String(date.getDate()).padStart(2, "0"),
String(date.getHours()).padStart(2, "0"),
String(date.getMinutes()).padStart(2, "0"),
];
const weekDay = ["日", "一", "二", "三", "四", "五", "六"][date.getDay()];
return {
date: `${year}-${month}-${day}`,
time: `${hour}:${minute}`,
week: `星期${weekDay}`,
};
},
},
};
</script>
sass样式
<style lang="scss">
.handle {
position: fixed;
bottom: 0;
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
font-size: 28rpx;
background: rgb(255, 255, 255);
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
}
.handle_ps,
.handle_card {
display: flex;
flex-direction: column;
text-align: center;
height: 75px;
line-height: 25px;
background: #ffffff;
padding: 0;
font-size: 25rpx;
margin: 15rpx 0;
}
.handle_ps,
.handle_card {
&::after {
border: none;
}
}
.handle_ps_image {
width: 50px;
height: 50px;
}
.handle_card_name {
font-size: 25rpx;
}
.handle_card_icon {
width: 40px;
height: 40px;
margin: 5px;
}
</style>
rotateBase64Img图片旋转方法
/**
* 图片旋转方法
* @param {string} src - 图片地址
* @param {number} edg - 旋转角度
* @param {function} callback - 回调函数
*/
export const rotateAndCropImage = (src, edg, callback) => {
if (edg % 90 !== 0) {
throw new Error("旋转角度必须是90的倍数!");
}
if (edg === 0) {
callback({ tempFilePath: src });
return;
}
const canvas = uni.createOffscreenCanvas({
type: "2d",
});
const ctx = canvas.getContext("2d");
const image = canvas.createImage();
image.crossOrigin = "anonymous";
image.src = src;
image.onload = () => {
const imgW = image.width;
const imgH = image.height;
if (imgW > 4096) {
showToast("图片宽度不能超过4096");
return;
}
const quadrant = (((edg / 90) % 4) + 4) % 4;
let rotatedWidth, rotatedHeight;
switch (quadrant) {
case 0:
case 2:
rotatedWidth = imgW;
rotatedHeight = imgH;
break;
case 1:
case 3:
rotatedWidth = imgH;
rotatedHeight = imgW;
break;
}
canvas.width = rotatedWidth;
canvas.height = rotatedHeight;
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((edg * Math.PI) / 180);
ctx.drawImage(image, -imgW / 2, -imgH / 2, imgW, imgH);
base64ToTempFilePath(canvas.toDataURL()).then((res) => {
callback(res);
});
};
};
删除临时文件方法
// 删除生成临时的文件
export const deleteWxFile = () => {
const fs = uni.getFileSystemManager();
fs.readdir({
dirPath: uni.env.USER_DATA_PATH,
success(res) {
res.files.forEach((el) => {
fs.unlink({
filePath: `${uni.env.USER_DATA_PATH}/${el}`,
});
});
fs.close();
},
});
};
base64图片转微信临时地址
// base64转换临时地址
export const base64ToTempFilePath = (base64Data) => {
const fs = uni.getFileSystemManager();
const fileName = "takes_photo_time" + Date.now() + ".png";
const filePath = uni.env.USER_DATA_PATH + "/" + fileName;
const base64 = base64Data.split(",")[1]; //移除固定头部data:image/png;base64,
const buffer = uni.base64ToArrayBuffer(base64);
return new Promise((resolve) => {
fs.writeFile({
filePath,
data: buffer,
encoding: "binary",
success() {
resolve(filePath);
},
});
});
};
感谢你的阅读,如对你有帮助请收藏+关注!
只分享干货实战和精品,从不啰嗦!!!
如某处不对请留言评论,欢迎指正~
博主可收徒、常玩QQ飞车,可一起来玩玩鸭~