更新记录

1.1.1(2025-01-11) 下载此版本

feat: 图片上传生成水印


平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
× × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × × × ×

插件使用说明

1. 按照步骤使用即可

第一步:

<u-form-item label-width="130" label="巡检照片:" prop="photoFiles">
    <u-upload :action="uploadUrl" upload-text="拍照" :source-type="['camera']" :file-list="fileList"
        :auto-upload="true" :before-upload="beforeUpload" @afterRead="afterRead" @on-progress=""
        @on-success="onSuccess" :on-error="onError" @on-preview="onPreview" @on-remove="onRemove"
        :header="headers" :name="name" :with-credentials="true" :show-upload-list="true" :multiple="true"
        :accept="accept" :tip="tip" />

    <view style="position: absolute;top: -999999px;">
        <view>
            <canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas"
                style="width: 2300px;height: 2000px;" />
        </view>
    </view>

</u-form-item>

第二步:

data() {
    return {

        waterMarkParams: {
            display: false, // 控制 canvas 创建与销毁
            canvasWidth: 300, // 默认宽度
            canvasHeight: 225, // 默认高度
        },

        photoFiles: [],

        uploadUrl: baseUrl + '/admin-api/infra/file/upload',
        fileList: [],
        headers: {
            Authorization: "Bearer " + getAccessToken()
        },

        name: 'file',
        accept: 'image/*',
        tip: '支持jpg/png/gif格式,大小不超过2M',
    }
}

第三步:

methods: {

    // 图片上传之前 水印 渲染
    beforeUpload(index, list) {
        console.log("beforeUpload: " + JSON.stringify(list));

        const areaName = this.checkinsOne.areaName;
        const streetName = this.checkinsOne.streetName;

        return new Promise((resolve, reject) => {

            // 入参 图片url,区名称,街道名称
            // 在这里添加水印
            this.addWaterMark(list[index].url, areaName, streetName)
                .then(watermarkedUrl => {
                    console.log('watermarkedUrl--->>>' + watermarkedUrl);
                    // 使用带有水印的图片 URL 替换原来的 file 对象中的 URL
                    list[index].url = watermarkedUrl;
                    resolve(list); // 继续上传
                }).catch(error => {
                    reject(error); // 中断上传
                });
        });
    },

    (event, file, fileList) {
        // 上传进度发生变化时的回调函数
        console.log(": " + JSON.stringify(file));
    },

    // 回调图片地址
    onSuccess(res, file, fileList) {
        console.log('上传成功--->>>' + JSON.stringify(res.data));

        this.photoFiles.push(res.data);
    },
    onError(err, file, fileList) {
        // 上传失败时的回调函数
        console.log("onError" + JSON.stringify(file));
    },
    onPreview(file) {
        console.log("onPreview " + JSON.stringify(file));

    },

    onRemove(file, fileList) {
        // 文件列表移除文件时的回调函数
        console.log("onRemove" + JSON.stringify(file));
        // 从model.photoFiles数组中移除对应的文件记录
        const index = this.model.photoFiles.findIndex(p => p.name === file.name);
        if (index !== -1) {
            this.removeFromList(index);
        }
    },
    removeFromList(index) {
        this.model.photoFiles.splice(index, 1);
    },
    resultFormatter(res) {
        // 返回结果的格式化函数
        console.log("resultFormatter" + JSON.stringify(file));
    },

    // 图片水印
    addWaterMark(src, areaName, streetName) {
        return new Promise((resolve, reject) => {
            // 获取图片信息,配置 canvas 尺寸
            uni.getImageInfo({
                src,
                success: res => {
                    // 修复部分手机(如红米9)手机屏幕比较窄拍摄出来的图片水印压缩着覆盖的问题
                    this.waterMarkParams.canvasWidth = Math.max(res.width, 886);
                    this.waterMarkParams.canvasHeight = res.height;
                    this.waterMarkParams.display = true
                    console.log('当前图片信息waterMarkParams:', this.waterMarkParams);
                    // 等待 canvas 元素创建
                    this.$nextTick(() => {
                        let context = uni.createCanvasContext("waterMarkCanvas", this);
                        /* 绘制 */
                        const {
                            canvasWidth,
                            canvasHeight
                        } = this.waterMarkParams
                        // 绘制前清空画布
                        context.clearRect(0, 0, canvasWidth, canvasHeight);
                        // 将图片src放到cancas内,宽高必须为图片大小
                        context.drawImage(src, 0, 0, canvasWidth, canvasHeight,
                            canvasWidth, canvasHeight);

                        // 在获取到canvas尺寸后计算偏移量
                        const offsetX = canvasWidth * 0.2; // 向左移动5%的宽度
                        const offsetY = canvasHeight * 0.05; // 向下移动5%的高度

                        // 防伪码的位置也根据新的偏移量调整
                        const fangweiY = 320 + offsetY;

                        // 动态设置日期和时间
                        const date = new Date();

                        // 更新文本绘制的位置
                        context.translate(offsetX, canvasHeight - (fangweiY + 15));

                        const formatDate =
                            `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;

                        const formatTime = String(date.getHours()).padStart(2, '0') +
                            ':' + String(
                                date.getMinutes()).padStart(2, '0');

                        const days = ['日', '一', '二', '三', '四', '五', '六'];
                        const day = days[date.getDay()];

                        // 水印信息文本绘制 areaName、formatTime、formatDate
                        fillText(context, -10, 120 + offsetY, areaName + '·' +
                            streetName,
                            'bold 45px "Microsoft YaHei"', 'white', 'center');

                        fillText(context, -55 + offsetX, 270 + offsetY, formatTime,
                            'bold 105px "Microsoft YaHei"', 'white', 'right');

                        fillRect(context, -40 + offsetX, 160 + offsetY, 6, 110,
                            '#346DFF');

                        fillText(context, -10 + offsetX, 210 + offsetY, formatDate,
                            'bold 32px "Microsoft YaHei"', 'white', 'left');

                        fillText(context, -10 + offsetX, 260 + offsetY, day + ' 晴 21℃',
                            'bold 32px "Microsoft YaHei"', 'white', 'left');

                        fillText(context, 120, fangweiY, `防伪:JY20240418160748XIAOMI`,
                            'bold 32px "Microsoft YaHei"', 'white', 'center');

                        // 对于marker图标的绘制也需要相应调整
                        fillCircle(context, -260 + offsetX, 30 + offsetY, 30,
                            '#346DFF');
                        fillCircle(context, -260 + offsetX, 30 + offsetY, 12, 'white');
                        fillTriangle(context, -260 + offsetX, 78 + offsetY, -235 +
                            offsetX, 48 + offsetY, -285 + offsetX, 48 + offsetY,
                            '#346DFF');

                        // 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
                        setTimeout(() => {
                            // 本次绘画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况
                            context.draw(false, () => {
                                console.log('!!!!!开始绘画', canvasWidth,
                                    canvasHeight);
                                uni.canvasToTempFilePath({
                                    canvasId: "waterMarkCanvas",
                                    fileType: "jpg",
                                    width: canvasWidth,
                                    height: canvasHeight,
                                    destWidth: canvasWidth,
                                    destHeight: canvasHeight,
                                    success: ({
                                        tempFilePath
                                    }) => {
                                        console.log('绘制成功',
                                            tempFilePath
                                        );
                                        this.waterMarkParams
                                            .display =
                                            false
                                        resolve(
                                            tempFilePath
                                        )
                                    },
                                    fail: err => {
                                        reject(err)
                                        console.log(err);
                                    }
                                }, this)
                            })
                        }, 1000);
                    })
                }
            })
        })

        // 绘制文字
        function fillText(context, x, y, content, font, fontStyle, textAlign) {
            // 保存当前绘图状态
            context.save();
            // 设置字体样式
            context.font = font
            // 设置文字颜色为白色
            context.fillStyle = fontStyle
            // 设置文字水平居中对齐
            context.textAlign = textAlign
            context.fillText(content, x, y)
            // 恢复到之前保存的绘图状态,清除样式设置
            context.restore();
        }

        // 绘制圆
        function fillCircle(context, x, y, r, fillStyle) {
            // 保存当前绘图状态
            context.save();
            context.beginPath();
            context.arc(x, y, r, 0, 2 * Math.PI);
            context.fillStyle = fillStyle;
            context.fill();
            context.closePath();
            // 恢复到之前保存的绘图状态,清除样式设置
            context.restore();
        }

        // 绘制三角形
        function fillTriangle(context, x1, y1, x2, y2, x3, y3, fillStyle) {
            // 保存当前绘图状态
            context.save();
            context.beginPath();
            context.moveTo(x1, y1);
            context.lineTo(x2, y2);
            context.lineTo(x3, y3);
            context.fillStyle = fillStyle;
            context.fill();
            context.closePath();
            // 恢复到之前保存的绘图状态,清除样式设置
            context.restore();
        }

        // 绘制矩形
        function fillRect(context, x, y, width, height, fillStyle) {
            // 保存当前绘图状态
            context.save();
            context.fillStyle = fillStyle;
            context.fillRect(x, y, width, height);
            // 恢复到之前保存的绘图状态,清除样式设置
            context.restore();
        }
    },
}

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问