ds: 墨水屏风格图片处理工具

• 42 分钟阅读 • deepseek · web

买了一个电子墨水屏摆件,可以显示黑、白、红三色图片,使用小程序上传图片,但未处理过的图片显示效果不好,因此用deepseek生成了一个处理工具,先把图片调整成三色图,再用小程序上传,效果就好多了。

ds生成

<!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>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: rgba(0, 0, 0, 0.7);
            border-radius: 15px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
        }
        
        header {
            text-align: center;
            margin-bottom: 30px;
            padding-bottom: 15px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            background: linear-gradient(to right, #ff9966, #ff5e62);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }
        
        .subtitle {
            font-size: 1.1rem;
            opacity: 0.8;
        }
        
        .app-container {
            display: flex;
            flex-wrap: wrap;
            gap: 30px;
        }
        
        .control-panel {
            flex: 1;
            min-width: 300px;
            background: rgba(30, 30, 40, 0.8);
            padding: 25px;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }
        
        .preview-panel {
            flex: 2;
            min-width: 500px;
            display: flex;
            flex-direction: column;
            gap: 25px;
        }
        
        .section {
            margin-bottom: 25px;
        }
        
        h2 {
            font-size: 1.4rem;
            margin-bottom: 15px;
            color: #ff9966;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        h2 i {
            font-size: 1.2rem;
        }
        
        .upload-area {
            border: 2px dashed #ff9966;
            border-radius: 10px;
            padding: 30px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
            background: rgba(255, 153, 102, 0.1);
        }
        
        .upload-area:hover {
            background: rgba(255, 153, 102, 0.2);
        }
        
        .upload-icon {
            font-size: 3rem;
            margin-bottom: 15px;
            color: #ff9966;
        }
        
        .slider-container {
            margin: 20px 0;
        }
        
        .slider-label {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
        }
        
        .slider {
            width: 100%;
            height: 8px;
            -webkit-appearance: none;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 5px;
            outline: none;
        }
        
        .slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: #ff5e62;
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .slider::-webkit-slider-thumb:hover {
            background: #ff9966;
            transform: scale(1.2);
        }
        
        .ratio-presets {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            margin-top: 15px;
        }
        
        .ratio-btn {
            padding: 8px 15px;
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 5px;
            color: white;
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .ratio-btn:hover {
            background: rgba(255, 153, 102, 0.3);
        }
        
        .ratio-btn.active {
            background: #ff5e62;
            border-color: #ff5e62;
        }
        
        .preview-container {
            background: rgba(30, 30, 40, 0.8);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
            flex: 1;
            display: flex;
            flex-direction: column;
        }
        
        .preview-title {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }
        
        .preview-area {
            flex: 1;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 8px;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            min-height: 300px;
        }
        
        .preview-image {
            max-width: 100%;
            max-height: 100%;
            display: none;
        }
        
        .effects-container {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
        }
        
        .effect-box {
            flex: 1;
            min-width: 200px;
            background: rgba(30, 30, 40, 0.8);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
            text-align: center;
        }
        
        .effect-title {
            font-size: 1.2rem;
            margin-bottom: 15px;
            color: #ff9966;
        }
        
        .effect-preview {
            width: 100%;
            height: 200px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 8px;
            display: flex;
            justify-content: center;
            align-items: center;
            margin-bottom: 15px;
            overflow: hidden;
        }
        
        .effect-image {
            max-width: 100%;
            max-height: 100%;
            display: none;
        }
        
        .btn {
            padding: 12px 25px;
            background: linear-gradient(to right, #ff9966, #ff5e62);
            border: none;
            border-radius: 8px;
            color: white;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }
        
        .btn:hover {
            transform: translateY(-3px);
            box-shadow: 0 5px 15px rgba(255, 94, 98, 0.4);
        }
        
        .btn:active {
            transform: translateY(0);
        }
        
        .btn-full {
            width: 100%;
            margin-top: 10px;
        }
        
        .hidden {
            display: none;
        }
        
        .placeholder-text {
            color: rgba(255, 255, 255, 0.5);
            font-style: italic;
        }
        
        .info-box {
            background: rgba(255, 153, 102, 0.1);
            border-left: 4px solid #ff9966;
            padding: 15px;
            border-radius: 5px;
            margin-top: 20px;
        }
        
        @media (max-width: 900px) {
            .app-container {
                flex-direction: column;
            }
            
            .control-panel, .preview-panel {
                min-width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>墨水屏风格图片处理工具</h1>
            <p class="subtitle">上传图片,按比例裁切,生成黑白红三色墨水屏效果</p>
        </header>
        
        <div class="app-container">
            <div class="control-panel">
                <div class="section">
                    <h2><i>📁</i> 图片上传</h2>
                    <div class="upload-area" id="uploadArea">
                        <div class="upload-icon">📁</div>
                        <p>点击或拖拽图片到此处上传</p>
                        <p class="placeholder-text">支持 JPG, PNG, GIF 格式</p>
                    </div>
                    <input type="file" id="fileInput" class="hidden" accept="image/*">
                </div>
                
                <div class="section">
                    <h2><i>✂️</i> 图片裁切</h2>
                    <div class="slider-container">
                        <div class="slider-label">
                            <span>宽度比例:</span>
                            <span id="widthValue">100%</span>
                        </div>
                        <input type="range" min="10" max="100" value="100" class="slider" id="widthSlider">
                    </div>
                    
                    <div class="slider-container">
                        <div class="slider-label">
                            <span>高度比例:</span>
                            <span id="heightValue">100%</span>
                        </div>
                        <input type="range" min="10" max="100" value="100" class="slider" id="heightSlider">
                    </div>
                    
                    <div class="ratio-presets">
                        <div class="ratio-btn active" data-ratio="1:1">1:1 (正方形)</div>
                        <div class="ratio-btn" data-ratio="16:9">16:9 (宽屏)</div>
                        <div class="ratio-btn" data-ratio="4:3">4:3 (标准)</div>
                        <div class="ratio-btn" data-ratio="3:2">3:2 (照片)</div>
                        <div class="ratio-btn" data-ratio="9:16">9:16 (手机)</div>
                    </div>
                </div>
                
                <div class="section">
                    <h2><i>🎨</i> 墨水屏效果</h2>
                    <div class="slider-container">
                        <div class="slider-label">
                            <span>红色阈值:</span>
                            <span id="redThresholdValue">50%</span>
                        </div>
                        <input type="range" min="1" max="100" value="50" class="slider" id="redThreshold">
                    </div>
                    
                    <div class="slider-container">
                        <div class="slider-label">
                            <span>黑白对比度:</span>
                            <span id="contrastValue">50%</span>
                        </div>
                        <input type="range" min="1" max="100" value="50" class="slider" id="contrast">
                    </div>
                    
                    <button class="btn btn-full" id="generateBtn">
                        <i>🔄</i> 生成墨水屏效果
                    </button>
                    <button class="btn btn-full" id="downloadBtn" style="margin-top: 15px;">
                        <i>💾</i> 下载墨水屏图片
                    </button>
                </div>
                
                <div class="info-box">
                    <h3>墨水屏效果说明</h3>
                    <p>此工具模拟只能显示黑色、白色和红色的墨水屏设备。</p>
                    <p>通过调整红色阈值和对比度,可以控制哪些区域显示为红色,哪些区域显示为黑白。</p>
                </div>
            </div>
            
            <div class="preview-panel">
                <div class="preview-container">
                    <div class="preview-title">
                        <h2><i>👁️</i> 原图预览</h2>
                        <button class="btn" id="resetBtn">
                            <i>🔄</i> 重置
                        </button>
                    </div>
                    <div class="preview-area">
                        <p class="placeholder-text" id="previewPlaceholder">上传的图片将显示在这里</p>
                        <img id="previewImage" class="preview-image" alt="预览图片">
                    </div>
                </div>
                
                <div class="effects-container">
                    <div class="effect-box">
                        <h3 class="effect-title">墨水屏效果预览</h3>
                        <div class="effect-preview">
                            <p class="placeholder-text">墨水屏效果预览</p>
                            <img id="einkImage" class="effect-image" alt="墨水屏效果">
                        </div>
                        <p style="font-size: 0.9rem; margin-top: 10px;">模拟三色墨水屏显示效果,仅使用黑、白、红三种颜色</p>
                    </div>
                    
                    <div class="effect-box">
                        <h3 class="effect-title">效果说明</h3>
                        <div class="effect-preview" style="background: #000; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white;">
                            <div style="display: flex; flex-direction: column; gap: 10px; text-align: center;">
                                <div style="background: #000; padding: 5px; border-radius: 3px;">黑色区域</div>
                                <div style="background: #fff; color: #000; padding: 5px; border-radius: 3px;">白色区域</div>
                                <div style="background: #f00; padding: 5px; border-radius: 3px;">红色区域</div>
                            </div>
                        </div>
                        <p style="font-size: 0.9rem; margin-top: 10px;">调整红色阈值和对比度参数可以优化显示效果</p>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // DOM元素
        const fileInput = document.getElementById('fileInput');
        const uploadArea = document.getElementById('uploadArea');
        const widthSlider = document.getElementById('widthSlider');
        const heightSlider = document.getElementById('heightSlider');
        const widthValue = document.getElementById('widthValue');
        const heightValue = document.getElementById('heightValue');
        const redThreshold = document.getElementById('redThreshold');
        const redThresholdValue = document.getElementById('redThresholdValue');
        const contrast = document.getElementById('contrast');
        const contrastValue = document.getElementById('contrastValue');
        const ratioBtns = document.querySelectorAll('.ratio-btn');
        const generateBtn = document.getElementById('generateBtn');
        const resetBtn = document.getElementById('resetBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const previewImage = document.getElementById('previewImage');
        const einkImage = document.getElementById('einkImage');
        const previewPlaceholder = document.getElementById('previewPlaceholder');
        
        // 全局变量
        let originalImage = null;
        let croppedImage = null;
        let einkCanvas = null; // 保存墨水屏效果的Canvas
        
        // 上传区域点击事件
        uploadArea.addEventListener('click', () => {
            fileInput.click();
        });
        
        // 拖放功能
        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.style.background = 'rgba(255, 153, 102, 0.3)';
        });
        
        uploadArea.addEventListener('dragleave', () => {
            uploadArea.style.background = 'rgba(255, 153, 102, 0.1)';
        });
        
        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.style.background = 'rgba(255, 153, 102, 0.1)';
            
            if (e.dataTransfer.files.length) {
                fileInput.files = e.dataTransfer.files;
                handleImageUpload(e.dataTransfer.files[0]);
            }
        });
        
        // 文件选择事件
        fileInput.addEventListener('change', (e) => {
            if (e.target.files.length) {
                handleImageUpload(e.target.files[0]);
            }
        });
        
        // 处理图片上传
        function handleImageUpload(file) {
            if (!file.type.match('image.*')) {
                alert('请上传图片文件!');
                return;
            }
            
            const reader = new FileReader();
            
            reader.onload = (e) => {
                originalImage = new Image();
                originalImage.onload = () => {
                    previewPlaceholder.style.display = 'none';
                    previewImage.style.display = 'block';
                    previewImage.src = e.target.result;
                    
                    // 重置裁切比例
                    widthSlider.value = 100;
                    heightSlider.value = 100;
                    widthValue.textContent = '100%';
                    heightValue.textContent = '100%';
                    
                    // 应用裁切
                    applyCrop();
                };
                originalImage.src = e.target.result;
            };
            
            reader.readAsDataURL(file);
        }
        
        // 更新滑块值显示
        widthSlider.addEventListener('input', () => {
            widthValue.textContent = `${widthSlider.value}%`;
            applyCrop();
        });
        
        heightSlider.addEventListener('input', () => {
            heightValue.textContent = `${heightSlider.value}%`;
            applyCrop();
        });
        
        redThreshold.addEventListener('input', () => {
            redThresholdValue.textContent = `${redThreshold.value}%`;
            generateEinkEffect();
        });
        
        contrast.addEventListener('input', () => {
            contrastValue.textContent = `${contrast.value}%`;
            generateEinkEffect();
        });
        
        // 应用裁切比例
        function applyCrop() {
            if (!originalImage) return;
            
            const widthRatio = widthSlider.value / 100;
            const heightRatio = heightSlider.value / 100;
            
            // 创建Canvas进行裁切
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            
            // 计算裁切后的尺寸
            const newWidth = originalImage.width * widthRatio;
            const newHeight = originalImage.height * heightRatio;
            
            // 计算裁切起点(居中裁切)
            const startX = (originalImage.width - newWidth) / 2;
            const startY = (originalImage.height - newHeight) / 2;
            
            canvas.width = newWidth;
            canvas.height = newHeight;
            
            ctx.drawImage(
                originalImage, 
                startX, startY, newWidth, newHeight,
                0, 0, newWidth, newHeight
            );
            
            // 更新预览图
            previewImage.src = canvas.toDataURL('image/png');
            
            // 保存裁切后的图片
            croppedImage = new Image();
            croppedImage.onload = () => {
                generateEinkEffect();
            };
            croppedImage.src = canvas.toDataURL('image/png');
        }
        
        // 预设比例按钮
        ratioBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                ratioBtns.forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
                
                const ratio = btn.getAttribute('data-ratio').split(':');
                const width = parseInt(ratio[0]);
                const height = parseInt(ratio[1]);
                
                // 根据原图尺寸和比例计算裁切比例
                if (originalImage) {
                    const imgRatio = originalImage.width / originalImage.height;
                    const targetRatio = width / height;
                    
                    if (imgRatio > targetRatio) {
                        // 图片更宽,调整高度比例
                        const newHeight = (originalImage.height * targetRatio / imgRatio) / originalImage.height * 100;
                        heightSlider.value = newHeight;
                        widthSlider.value = 100;
                        heightValue.textContent = `${Math.round(newHeight)}%`;
                        widthValue.textContent = '100%';
                    } else {
                        // 图片更高,调整宽度比例
                        const newWidth = (originalImage.width * imgRatio / targetRatio) / originalImage.width * 100;
                        widthSlider.value = newWidth;
                        heightSlider.value = 100;
                        widthValue.textContent = `${Math.round(newWidth)}%`;
                        heightValue.textContent = '100%';
                    }
                    
                    applyCrop();
                }
            });
        });
        
        // 生成墨水屏效果
        function generateEinkEffect() {
            if (!croppedImage) return;
            
            // 创建Canvas用于墨水屏效果
            einkCanvas = document.createElement('canvas');
            const ctx = einkCanvas.getContext('2d');
            einkCanvas.width = croppedImage.width;
            einkCanvas.height = croppedImage.height;
            
            ctx.drawImage(croppedImage, 0, 0);
            const imageData = ctx.getImageData(0, 0, einkCanvas.width, einkCanvas.height);
            const data = imageData.data;
            
            // 获取阈值和对比度值
            const redThresholdValue = redThreshold.value / 100;
            const contrastValue = contrast.value / 100 * 2; // 将0-1映射到0-2
            
            for (let i = 0; i < data.length; i += 4) {
                const r = data[i];
                const g = data[i+1];
                const b = data[i+2];
                
                // 计算亮度
                let brightness = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
                
                // 应用对比度
                brightness = (brightness - 0.5) * contrastValue + 0.5;
                brightness = Math.max(0, Math.min(1, brightness));
                
                // 计算红色强度
                const redIntensity = r / 255;
                const greenIntensity = g / 255;
                const blueIntensity = b / 255;
                
                // 判断是否为红色区域
                const isRed = redIntensity > greenIntensity * 1.5 && 
                              redIntensity > blueIntensity * 1.5 && 
                              redIntensity > redThresholdValue;
                
                if (isRed) {
                    // 红色区域
                    data[i] = 255;     // R
                    data[i+1] = 0;     // G
                    data[i+2] = 0;     // B
                } else {
                    // 黑白区域
                    const bwValue = brightness > 0.5 ? 255 : 0;
                    data[i] = bwValue;     // R
                    data[i+1] = bwValue;   // G
                    data[i+2] = bwValue;   // B
                }
            }
            
            ctx.putImageData(imageData, 0, 0);
            einkImage.style.display = 'block';
            einkImage.src = einkCanvas.toDataURL('image/png');
        }
        
        // 生成效果按钮
        generateBtn.addEventListener('click', generateEinkEffect);
        
        // 重置按钮
        resetBtn.addEventListener('click', () => {
            if (originalImage) {
                widthSlider.value = 100;
                heightSlider.value = 100;
                widthValue.textContent = '100%';
                heightValue.textContent = '100%';
                redThreshold.value = 50;
                redThresholdValue.textContent = '50%';
                contrast.value = 50;
                contrastValue.textContent = '50%';
                ratioBtns[0].classList.add('active');
                for (let i = 1; i < ratioBtns.length; i++) {
                    ratioBtns[i].classList.remove('active');
                }
                
                previewImage.src = originalImage.src;
                croppedImage = originalImage;
                generateEinkEffect();
            }
        });
        
        // 下载功能 - 修复版
        downloadBtn.addEventListener('click', () => {
            if (!einkCanvas) {
                alert('请先生成墨水屏效果图!');
                return;
            }
            
            // 创建下载链接
            const link = document.createElement('a');
            link.download = '墨水屏效果图.png';
            link.href = einkCanvas.toDataURL('image/png');
            
            // 添加到文档并触发点击
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        });
    </script>
</body>
</html>

墨水屏图片上传程序

项目:https://tsl0922.github.io/EPD-nRF5/
需要梯子才可访问。文末为国内可用版本。

体验

图片处理效果:

小程序入口:

图片处理工具地址:https://api.qs100371.top/zm.html
图片上传工具地址:https://api.qs100371.top/msp.html(需用edge浏览器)

文章标签: deepseek, web

上一篇 : FetchV视频下载扩展
下一篇 : frp: 给内网web服务添加https支持
留言
阅读进度 0%