html5纯前端实现视频添加图片水印

105 阅读1分钟
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>视频加水印工具</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .config-area {
            margin: 20px 0;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .config-area div {
            margin: 10px 0;
        }
        .config-area label {
            display: inline-block;
            width: 180px;
        }
        .config-area input {
            padding: 5px;
            width: 80px;
        }
        .upload-area {
            margin-bottom: 20px;
            padding: 20px;
            border: 2px dashed #ccc;
            text-align: center;
        }
        button {
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
            margin: 10px 0;
        }
        button:disabled {
            background-color: #cccccc;
        }
        #downloadLink {
            display: none;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <h1>视频加水印工具</h1>
    
    <div class="upload-area">
        <h3>上传视频文件</h3>
        <input type="file" id="videoInput" accept="video/*">
    </div>
    
    <div class="upload-area">
        <h3>上传水印图片</h3>
        <input type="file" id="watermarkInput" accept="image/*">
    </div>
    
    <div class="config-area">
        <h3>水印配置</h3>
        <div>
            <label>距离左侧(px):</label>
            <input type="number" id="offsetLeft" value="20" min="0">
        </div>
        <div>
            <label>距离底部(px):</label>
            <input type="number" id="offsetBottom" value="20" min="0">
        </div>
        <div>
            <label>水印高度(px):</label>
            <input type="number" id="watermarkHeight" value="50" min="10">
        </div>
    </div>
    
    <button id="processBtn" disabled>处理视频</button>
    
    <div id="status"></div>
    <div id="progress" style="margin: 10px 0; font-size: 16px;"></div>
    
    <a id="downloadLink" download="watermarked_video.mp4">下载处理后的视频</a>

    <script>
        // 全局变量
        let videoFile = null;
        let watermarkFile = null;
        let mediaRecorder = null;
        let recordedChunks = [];
        
        // 获取DOM元素
        const videoInput = document.getElementById('videoInput');
        const watermarkInput = document.getElementById('watermarkInput');
        const processBtn = document.getElementById('processBtn');
        const downloadLink = document.getElementById('downloadLink');
        const statusDiv = document.getElementById('status');
        
        // 事件监听
        videoInput.addEventListener('change', handleVideoUpload);
        watermarkInput.addEventListener('change', handleWatermarkUpload);
        processBtn.addEventListener('click', processVideo);
        
        // 处理视频上传
        function handleVideoUpload(e) {
            videoFile = e.target.files[0];
            checkReadyState();
        }
        
        // 处理水印上传
        function handleWatermarkUpload(e) {
            watermarkFile = e.target.files[0];
            checkReadyState();
        }
        
        // 检查是否准备好处理
        function checkReadyState() {
            if (videoFile && watermarkFile) {
                processBtn.disabled = false;
            } else {
                processBtn.disabled = true;
            }
        }
        
        // 处理视频加水印
        function processVideo() {
            statusDiv.textContent = '处理中...';
            document.getElementById('progress').textContent = '0%';
            processBtn.disabled = true;
            
            // 创建视频元素
            const video = document.createElement('video');
            video.src = URL.createObjectURL(videoFile);
            
            // 创建Canvas
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            
            // 加载水印图片
            const watermarkImg = new Image();
            watermarkImg.src = URL.createObjectURL(watermarkFile);
            
            watermarkImg.onload = function() {
                video.onloadedmetadata = function() {
                    canvas.width = video.videoWidth;
                    canvas.height = video.videoHeight;
                    
                    // 设置MediaRecorder
                    const stream = canvas.captureStream();
                    const mimeType = MediaRecorder.isTypeSupported('video/mp4')
                        ? 'video/mp4; codecs="avc1.42E01E"'
                        : 'video/webm';
                    
                    mediaRecorder = new MediaRecorder(stream, {
                        mimeType: mimeType
                    });
                    
                    mediaRecorder.ondataavailable = function(e) {
                        if (e.data.size > 0) {
                            recordedChunks.push(e.data);
                        }
                    };
                    
                    mediaRecorder.onstop = function() {
                        const fileExtension = mimeType.includes('mp4') ? 'mp4' : 'webm';
                        const blob = new Blob(recordedChunks, {
                            type: mimeType
                        });
                        downloadLink.href = URL.createObjectURL(blob);
                        downloadLink.download = `watermarked_video.${fileExtension}`;
                        downloadLink.style.display = 'block';
                        statusDiv.textContent = `处理完成!格式: ${fileExtension}`;
                    };
                    
                    mediaRecorder.start();
                    
                    // 绘制视频帧和水印
                    video.addEventListener('play', function() {
                        function drawFrame() {
                            if (video.paused || video.ended) {
                                mediaRecorder.stop();
                                return;
                            }
                            
                            // 更新进度
                            const progress = Math.round((video.currentTime / video.duration) * 100);
                            document.getElementById('progress').textContent = `${progress}%`;
                            
                            // 绘制视频帧
                            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
                            
                            // 绘制水印(使用配置参数)
                            const offsetLeft = parseInt(document.getElementById('offsetLeft').value);
                            const offsetBottom = parseInt(document.getElementById('offsetBottom').value);
                            const desiredHeight = parseInt(document.getElementById('watermarkHeight').value);
                            const scale = desiredHeight / watermarkImg.height;
                            const watermarkWidth = watermarkImg.width * scale;
                            const watermarkHeight = desiredHeight;
                            const xPos = offsetLeft;
                            const yPos = canvas.height - watermarkHeight - offsetBottom;
                            
                            ctx.drawImage(
                                watermarkImg,
                                xPos, yPos,
                                watermarkWidth, watermarkHeight
                            );
                            
                            requestAnimationFrame(drawFrame);
                        }
                        
                        drawFrame();
                    });
                    
                    video.play();
                };
            };
        }
    </script>
</body>
</html>