deepseek v3-0324实现SVG 编辑器

SVG 编辑器 HTML

以下是一个简单的基于HTML/JavaScript的SVG编辑器代码。这个编辑器允许你绘制基本形状(矩形、圆形、线条等),修改它们的属性,并导出SVG代码。
在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简易SVG编辑器</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            height: 100vh;
        }
        .container {
            display: flex;
            flex: 1;
            gap: 20px;
        }
        #toolbar {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            padding: 10px;
            background: #f0f0f0;
            border-radius: 5px;
        }
        button {
            padding: 8px 12px;
            cursor: pointer;
        }
        #editor-panel {
            flex: 3;
            border: 1px solid #ccc;
            position: relative;
        }
        #svg-canvas {
            width: 100%;
            height: 100%;
            background-color: white;
        }
        #properties-panel {
            flex: 1;
            border: 1px solid #ccc;
            padding: 15px;
            overflow-y: auto;
        }
        .property-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input, select {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            box-sizing: border-box;
        }
        #svg-code {
            width: 100%;
            height: 200px;
            margin-top: 20px;
            font-family: monospace;
        }
        .shape {
            cursor: move;
        }
        .shape.selected {
            outline: 2px dashed #0066ff;
        }
    </style>
</head>
<body>
    <h1>简易SVG编辑器</h1>
    
    <div id="toolbar">
        <button id="select-btn">选择</button>
        <button id="rect-btn">矩形</button>
        <button id="circle-btn">圆形</button>
        <button id="ellipse-btn">椭圆</button>
        <button id="line-btn">线条</button>
        <button id="text-btn">文本</button>
        <button id="delete-btn">删除</button>
        <button id="export-btn">导出SVG</button>
    </div>
    
    <div class="container">
        <div id="editor-panel">
            <svg id="svg-canvas" xmlns="http://www.w3.org/2000/svg"></svg>
        </div>
        
        <div id="properties-panel">
            <h3>属性编辑器</h3>
            <div id="shape-properties">
                <p>选择一个元素来编辑其属性</p>
            </div>
            
            <h3>SVG代码</h3>
            <textarea id="svg-code" readonly></textarea>
            <button id="copy-code-btn">复制代码</button>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const svgCanvas = document.getElementById('svg-canvas');
            const shapeProperties = document.getElementById('shape-properties');
            const svgCodeTextarea = document.getElementById('svg-code');
            let selectedElement = null;
            let isDrawing = false;
            let currentTool = 'select';
            let startX, startY;
            
            // 工具按钮事件监听
            document.getElementById('select-btn').addEventListener('click', () => currentTool = 'select');
            document.getElementById('rect-btn').addEventListener('click', () => currentTool = 'rect');
            document.getElementById('circle-btn').addEventListener('click', () => currentTool = 'circle');
            document.getElementById('ellipse-btn').addEventListener('click', () => currentTool = 'ellipse');
            document.getElementById('line-btn').addEventListener('click', () => currentTool = 'line');
            document.getElementById('text-btn').addEventListener('click', () => currentTool = 'text');
            
            document.getElementById('delete-btn').addEventListener('click', deleteSelected);
            document.getElementById('export-btn').addEventListener('click', exportSVG);
            document.getElementById('copy-code-btn').addEventListener('click', copySVGCode);
            
            // SVG画布事件监听
            svgCanvas.addEventListener('mousedown', startDrawing);
            svgCanvas.addEventListener('mousemove', draw);
            svgCanvas.addEventListener('mouseup', endDrawing);
            svgCanvas.addEventListener('mouseleave', endDrawing);
            
            // 开始绘制
            function startDrawing(e) {
                if (currentTool === 'select') {
                    const target = e.target.closest('.shape');
                    if (target) {
                        selectElement(target);
                    } else {
                        deselectElement();
                    }
                    return;
                }
                
                isDrawing = true;
                const rect = svgCanvas.getBoundingClientRect();
                startX = e.clientX - rect.left;
                startY = e.clientY - rect.top;
                
                if (currentTool === 'text') {
                    createText(startX, startY);
                    isDrawing = false;
                }
            }
            
            // 绘制过程中
            function draw(e) {
                if (!isDrawing || currentTool === 'select' || currentTool === 'text') return;
                
                const rect = svgCanvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                
                if (!selectedElement) {
                    // 创建新元素
                    switch (currentTool) {
                        case 'rect':
                            selectedElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
                            break;
                        case 'circle':
                            selectedElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                            break;
                        case 'ellipse':
                            selectedElement = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
                            break;
                        case 'line':
                            selectedElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');
                            selectedElement.setAttribute('x1', startX);
                            selectedElement.setAttribute('y1', startY);
                            break;
                    }
                    
                    if (selectedElement) {
                        selectedElement.classList.add('shape');
                        svgCanvas.appendChild(selectedElement);
                        updateShapeProperties();
                    }
                }
                
                // 更新元素属性
                if (selectedElement) {
                    switch (currentTool) {
                        case 'rect':
                            selectedElement.setAttribute('x', Math.min(startX, x));
                            selectedElement.setAttribute('y', Math.min(startY, y));
                            selectedElement.setAttribute('width', Math.abs(x - startX));
                            selectedElement.setAttribute('height', Math.abs(y - startY));
                            break;
                        case 'circle':
                            const radius = Math.sqrt(Math.pow(x - startX, 2) + Math.pow(y - startY, 2));
                            selectedElement.setAttribute('cx', startX);
                            selectedElement.setAttribute('cy', startY);
                            selectedElement.setAttribute('r', radius);
                            break;
                        case 'ellipse':
                            const rx = Math.abs(x - startX);
                            const ry = Math.abs(y - startY);
                            selectedElement.setAttribute('cx', startX);
                            selectedElement.setAttribute('cy', startY);
                            selectedElement.setAttribute('rx', rx);
                            selectedElement.setAttribute('ry', ry);
                            break;
                        case 'line':
                            selectedElement.setAttribute('x2', x);
                            selectedElement.setAttribute('y2', y);
                            break;
                    }
                    
                    // 设置默认样式
                    if (!selectedElement.getAttribute('fill')) {
                        selectedElement.setAttribute('fill', 'blue');
                    }
                    if (!selectedElement.getAttribute('stroke')) {
                        selectedElement.setAttribute('stroke', 'black');
                    }
                    if (!selectedElement.getAttribute('stroke-width')) {
                        selectedElement.setAttribute('stroke-width', '2');
                    }
                    
                    updateShapeProperties();
                }
            }
            
            // 结束绘制
            function endDrawing() {
                isDrawing = false;
            }
            
            // 创建文本元素
            function createText(x, y) {
                deselectElement();
                selectedElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                selectedElement.classList.add('shape');
                selectedElement.setAttribute('x', x);
                selectedElement.setAttribute('y', y);
                selectedElement.setAttribute('fill', 'black');
                selectedElement.textContent = '双击编辑文本';
                svgCanvas.appendChild(selectedElement);
                
                selectedElement.addEventListener('dblclick', function() {
                    const newText = prompt('输入文本:', this.textContent);
                    if (newText !== null) {
                        this.textContent = newText;
                        updateShapeProperties();
                        updateSVGCode();
                    }
                });
                
                selectElement(selectedElement);
                updateShapeProperties();
                updateSVGCode();
            }
            
            // 选择元素
            function selectElement(element) {
                deselectElement();
                selectedElement = element;
                selectedElement.classList.add('selected');
                updateShapeProperties();
                updateSVGCode();
                
                // 添加拖拽功能
                let isDragging = false;
                let offsetX, offsetY;
                
                selectedElement.addEventListener('mousedown', startDrag);
                
                function startDrag(e) {
                    if (currentTool !== 'select') return;
                    
                    isDragging = true;
                    const rect = selectedElement.getBoundingClientRect();
                    offsetX = e.clientX - rect.left;
                    offsetY = e.clientY - rect.top;
                    
                    e.preventDefault(); // 防止文本选择
                }
                
                svgCanvas.addEventListener('mousemove', drag);
                svgCanvas.addEventListener('mouseup', endDrag);
                svgCanvas.addEventListener('mouseleave', endDrag);
                
                function drag(e) {
                    if (!isDragging || !selectedElement) return;
                    
                    const rect = svgCanvas.getBoundingClientRect();
                    const x = e.clientX - rect.left - offsetX;
                    const y = e.clientY - rect.top - offsetY;
                    
                    if (selectedElement.tagName === 'text') {
                        selectedElement.setAttribute('x', x);
                        selectedElement.setAttribute('y', y);
                    } else if (selectedElement.tagName === 'circle') {
                        selectedElement.setAttribute('cx', x + offsetX);
                        selectedElement.setAttribute('cy', y + offsetY);
                    } else if (selectedElement.tagName === 'ellipse') {
                        selectedElement.setAttribute('cx', x + offsetX);
                        selectedElement.setAttribute('cy', y + offsetY);
                    } else if (selectedElement.tagName === 'line') {
                        const x1 = parseFloat(selectedElement.getAttribute('x1'));
                        const y1 = parseFloat(selectedElement.getAttribute('y1'));
                        const x2 = parseFloat(selectedElement.getAttribute('x2'));
                        const y2 = parseFloat(selectedElement.getAttribute('y2'));
                        
                        const dx = x - (x1 + x2) / 2 + offsetX;
                        const dy = y - (y1 + y2) / 2 + offsetY;
                        
                        selectedElement.setAttribute('x1', x1 + dx);
                        selectedElement.setAttribute('y1', y1 + dy);
                        selectedElement.setAttribute('x2', x2 + dx);
                        selectedElement.setAttribute('y2', y2 + dy);
                    } else if (selectedElement.tagName === 'rect') {
                        selectedElement.setAttribute('x', x);
                        selectedElement.setAttribute('y', y);
                    }
                    
                    updateShapeProperties();
                    updateSVGCode();
                }
                
                function endDrag() {
                    isDragging = false;
                }
            }
            
            // 取消选择元素
            function deselectElement() {
                if (selectedElement) {
                    selectedElement.classList.remove('selected');
                    selectedElement = null;
                    shapeProperties.innerHTML = '<p>选择一个元素来编辑其属性</p>';
                }
            }
            
            // 删除选中元素
            function deleteSelected() {
                if (selectedElement) {
                    svgCanvas.removeChild(selectedElement);
                    deselectElement();
                    updateSVGCode();
                }
            }
            
            // 更新形状属性面板
            function updateShapeProperties() {
                if (!selectedElement) return;
                
                let html = '<div class="property-group">';
                html += `<h4>${selectedElement.tagName}</h4>`;
                
                // 通用属性
                html += `
                    <label>填充颜色</label>
                    <input type="color" id="fill-color" value="${selectedElement.getAttribute('fill') || 'none'}">
                    
                    <label>描边颜色</label>
                    <input type="color" id="stroke-color" value="${selectedElement.getAttribute('stroke') || 'none'}">
                    
                    <label>描边宽度</label>
                    <input type="number" id="stroke-width" value="${selectedElement.getAttribute('stroke-width') || '1'}" min="0" step="0.5">
                `;
                
                // 特定属性
                switch (selectedElement.tagName) {
                    case 'rect':
                        html += `
                            <label>X坐标</label>
                            <input type="number" id="rect-x" value="${selectedElement.getAttribute('x')}" step="0.1">
                            
                            <label>Y坐标</label>
                            <input type="number" id="rect-y" value="${selectedElement.getAttribute('y')}" step="0.1">
                            
                            <label>宽度</label>
                            <input type="number" id="rect-width" value="${selectedElement.getAttribute('width')}" min="0" step="0.1">
                            
                            <label>高度</label>
                            <input type="number" id="rect-height" value="${selectedElement.getAttribute('height')}" min="0" step="0.1">
                        `;
                        break;
                    case 'circle':
                        html += `
                            <label>中心X</label>
                            <input type="number" id="circle-cx" value="${selectedElement.getAttribute('cx')}" step="0.1">
                            
                            <label>中心Y</label>
                            <input type="number" id="circle-cy" value="${selectedElement.getAttribute('cy')}" step="0.1">
                            
                            <label>半径</label>
                            <input type="number" id="circle-r" value="${selectedElement.getAttribute('r')}" min="0" step="0.1">
                        `;
                        break;
                    case 'ellipse':
                        html += `
                            <label>中心X</label>
                            <input type="number" id="ellipse-cx" value="${selectedElement.getAttribute('cx')}" step="0.1">
                            
                            <label>中心Y</label>
                            <input type="number" id="ellipse-cy" value="${selectedElement.getAttribute('cy')}" step="0.1">
                            
                            <label>X半径</label>
                            <input type="number" id="ellipse-rx" value="${selectedElement.getAttribute('rx')}" min="0" step="0.1">
                            
                            <label>Y半径</label>
                            <input type="number" id="ellipse-ry" value="${selectedElement.getAttribute('ry')}" min="0" step="0.1">
                        `;
                        break;
                    case 'line':
                        html += `
                            <label>起点X</label>
                            <input type="number" id="line-x1" value="${selectedElement.getAttribute('x1')}" step="0.1">
                            
                            <label>起点Y</label>
                            <input type="number" id="line-y1" value="${selectedElement.getAttribute('y1')}" step="0.1">
                            
                            <label>终点X</label>
                            <input type="number" id="line-x2" value="${selectedElement.getAttribute('x2')}" step="0.1">
                            
                            <label>终点Y</label>
                            <input type="number" id="line-y2" value="${selectedElement.getAttribute('y2')}" step="0.1">
                        `;
                        break;
                    case 'text':
                        html += `
                            <label>X坐标</label>
                            <input type="number" id="text-x" value="${selectedElement.getAttribute('x')}" step="0.1">
                            
                            <label>Y坐标</label>
                            <input type="number" id="text-y" value="${selectedElement.getAttribute('y')}" step="0.1">
                            
                            <label>文本内容</label>
                            <input type="text" id="text-content" value="${selectedElement.textContent}">
                            
                            <label>字体大小</label>
                            <input type="number" id="text-font-size" value="${selectedElement.getAttribute('font-size') || '16'}" min="1">
                        `;
                        break;
                }
                
                html += '</div>';
                shapeProperties.innerHTML = html;
                
                // 添加事件监听器
                document.getElementById('fill-color').addEventListener('input', function() {
                    selectedElement.setAttribute('fill', this.value);
                    updateSVGCode();
                });
                
                document.getElementById('stroke-color').addEventListener('input', function() {
                    selectedElement.setAttribute('stroke', this.value);
                    updateSVGCode();
                });
                
                document.getElementById('stroke-width').addEventListener('input', function() {
                    selectedElement.setAttribute('stroke-width', this.value);
                    updateSVGCode();
                });
                
                // 特定属性事件监听
                switch (selectedElement.tagName) {
                    case 'rect':
                        document.getElementById('rect-x').addEventListener('input', function() {
                            selectedElement.setAttribute('x', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('rect-y').addEventListener('input', function() {
                            selectedElement.setAttribute('y', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('rect-width').addEventListener('input', function() {
                            selectedElement.setAttribute('width', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('rect-height').addEventListener('input', function() {
                            selectedElement.setAttribute('height', this.value);
                            updateSVGCode();
                        });
                        break;
                    case 'circle':
                        document.getElementById('circle-cx').addEventListener('input', function() {
                            selectedElement.setAttribute('cx', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('circle-cy').addEventListener('input', function() {
                            selectedElement.setAttribute('cy', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('circle-r').addEventListener('input', function() {
                            selectedElement.setAttribute('r', this.value);
                            updateSVGCode();
                        });
                        break;
                    case 'ellipse':
                        document.getElementById('ellipse-cx').addEventListener('input', function() {
                            selectedElement.setAttribute('cx', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('ellipse-cy').addEventListener('input', function() {
                            selectedElement.setAttribute('cy', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('ellipse-rx').addEventListener('input', function() {
                            selectedElement.setAttribute('rx', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('ellipse-ry').addEventListener('input', function() {
                            selectedElement.setAttribute('ry', this.value);
                            updateSVGCode();
                        });
                        break;
                    case 'line':
                        document.getElementById('line-x1').addEventListener('input', function() {
                            selectedElement.setAttribute('x1', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('line-y1').addEventListener('input', function() {
                            selectedElement.setAttribute('y1', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('line-x2').addEventListener('input', function() {
                            selectedElement.setAttribute('x2', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('line-y2').addEventListener('input', function() {
                            selectedElement.setAttribute('y2', this.value);
                            updateSVGCode();
                        });
                        break;
                    case 'text':
                        document.getElementById('text-x').addEventListener('input', function() {
                            selectedElement.setAttribute('x', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('text-y').addEventListener('input', function() {
                            selectedElement.setAttribute('y', this.value);
                            updateSVGCode();
                        });
                        document.getElementById('text-content').addEventListener('input', function() {
                            selectedElement.textContent = this.value;
                            updateSVGCode();
                        });
                        document.getElementById('text-font-size').addEventListener('input', function() {
                            selectedElement.setAttribute('font-size', this.value);
                            updateSVGCode();
                        });
                        break;
                }
            }
            
            // 更新SVG代码显示
            function updateSVGCode() {
                const serializer = new XMLSerializer();
                const svgString = serializer.serializeToString(svgCanvas);
                svgCodeTextarea.value = svgString;
            }
            
            // 导出SVG
            function exportSVG() {
                updateSVGCode();
                const blob = new Blob([svgCodeTextarea.value], {type: 'image/svg+xml'});
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = 'drawing.svg';
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }
            
            // 复制SVG代码
            function copySVGCode() {
                updateSVGCode();
                svgCodeTextarea.select();
                document.execCommand('copy');
                alert('SVG代码已复制到剪贴板');
            }
        });
    </script>
</body>
</html>

功能说明

这个SVG编辑器具有以下功能:

  1. 绘图工具

    • 选择工具:选择和移动现有元素
    • 矩形工具:绘制矩形
    • 圆形工具:绘制圆形
    • 椭圆工具:绘制椭圆
    • 线条工具:绘制直线
    • 文本工具:添加文本
  2. 编辑功能

    • 修改元素的属性(位置、大小、颜色等)
    • 双击文本元素可编辑文本内容
    • 删除选中的元素
  3. 导出功能

    • 查看生成的SVG代码
    • 复制SVG代码到剪贴板
    • 导出SVG文件

使用方法

  1. 选择左侧工具栏中的绘图工具
  2. 在画布上点击并拖动来绘制形状
  3. 使用选择工具点击元素来选中它
  4. 在右侧属性面板中修改选中元素的属性
  5. 使用导出按钮保存您的SVG作品

您可以将此代码保存为HTML文件并在浏览器中打开它来使用这个SVG编辑器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值