<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas 右侧导航面板预览插件</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
overflow: hidden;
}
#main-container {
display: flex;
height: 100vh;
}
#canvas-container {
flex: 1;
position: relative;
overflow: hidden;
}
canvas {
display: block;
background-color: #f0f0f0;
}
#nav-panel {
width: 300px;
background-color: #2c3e50;
color: white;
padding: 15px;
box-sizing: border-box;
overflow-y: auto;
position: relative;
transition: transform 0.3s ease;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.2);
}
#drag-handle {
width: 10px;
height: 100%;
background-color: #34495e;
position: absolute;
left: 0;
top: 0;
cursor: col-resize;
}
.panel-section {
margin-bottom: 20px;
border-bottom: 1px solid #34495e;
padding-bottom: 15px;
}
.panel-section h3 {
margin-top: 0;
color: #ecf0f1;
}
.thumbnail {
width: 100%;
height: 120px;
background-color: #34495e;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
overflow: hidden;
}
.thumbnail:hover {
transform: scale(1.02);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
.thumbnail.active {
border: 2px solid #3498db;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
margin-bottom: 10px;
}
button:hover {
background-color: #2980b9;
}
.color-picker {
display: flex;
flex-wrap: wrap;
margin-bottom: 15px;
}
.color-option {
width: 30px;
height: 30px;
margin: 5px;
cursor: pointer;
border: 2px solid transparent;
}
.color-option:hover, .color-option.selected {
border-color: white;
}
#toggle-panel {
position: absolute;
top: 10px;
right: 10px;
background-color: #2c3e50;
color: white;
border: none;
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
z-index: 100;
display: none;
}
@media (max-width: 768px) {
#nav-panel {
position: absolute;
right: 0;
top: 0;
height: 100%;
transform: translateX(100%);
z-index: 99;
}
#nav-panel.open {
transform: translateX(0);
}
#toggle-panel {
display: block;
}
}
</style>
</head>
<body>
<div id="main-container">
<div id="canvas-container">
<canvas id="main-canvas"></canvas>
<button id="toggle-panel">≡</button>
</div>
<div id="nav-panel">
<div id="drag-handle"></div>
<div class="panel-section">
<h3>预览导航</h3>
<div id="thumbnails-container">
</div>
</div>
<div class="panel-section">
<h3>工具</h3>
<button id="clear-btn">清空画布</button>
<button id="save-btn">保存图像</button>
</div>
<div class="panel-section">
<h3>画笔设置</h3>
<label for="brush-size">画笔大小:</label>
<input type="range" id="brush-size" min="1" max="50" value="5">
<span id="brush-size-value">5</span>
<h4>颜色选择:</h4>
<div class="color-picker">
<div class="color-option selected" style="background-color: #000000;" data-color="#000000"></div>
<div class="color-option" style="background-color: #ff0000;" data-color="#ff0000"></div>
<div class="color-option" style="background-color: #00ff00;" data-color="#00ff00"></div>
<div class="color-option" style="background-color: #0000ff;" data-color="#0000ff"></div>
<div class="color-option" style="background-color: #ffff00;" data-color="#ffff00"></div>
<div class="color-option" style="background-color: #ff00ff;" data-color="#ff00ff"></div>
<div class="color-option" style="background-color: #00ffff;" data-color="#00ffff"></div>
<div class="color-option" style="background-color: #ffffff;" data-color="#ffffff"></div>
</div>
</div>
<div class="panel-section">
<h3>关于</h3>
<p>这是一个Canvas绘图工具,带有右侧导航面板预览功能。</p>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
const canvas = document.getElementById('main-canvas');
const ctx = canvas.getContext('2d');
const navPanel = document.getElementById('nav-panel');
const dragHandle = document.getElementById('drag-handle');
const togglePanelBtn = document.getElementById('toggle-panel');
const clearBtn = document.getElementById('clear-btn');
const saveBtn = document.getElementById('save-btn');
const brushSizeInput = document.getElementById('brush-size');
const brushSizeValue = document.getElementById('brush-size-value');
const colorOptions = document.querySelectorAll('.color-option');
const thumbnailsContainer = document.getElementById('thumbnails-container');
// 设置Canvas大小
function resizeCanvas() {
const container = document.getElementById('canvas-container');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
// 重绘内容
if (localStorage.getItem('canvasData')) {
restoreCanvas();
}
// 更新缩略图
updateThumbnails();
}
// 初始化Canvas
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 绘图状态
let isDrawing = false;
let lastX = 0;
let lastY = 0;
let currentColor = '#000000';
let currentBrushSize = 5;
// 设置画笔颜色
colorOptions.forEach(option => {
option.addEventListener('click', function() {
colorOptions.forEach(opt => opt.classList.remove('selected'));
this.classList.add('selected');
currentColor = this.dataset.color;
});
});
// 设置画笔大小
brushSizeInput.addEventListener('input', function() {
currentBrushSize = this.value;
brushSizeValue.textContent = this.value;
});
// 绘图函数
function draw(e) {
if (!isDrawing) return;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.strokeStyle = currentColor;
ctx.lineWidth = currentBrushSize;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
// 保存Canvas状态
saveCanvasState();
// 更新缩略图
updateThumbnails();
}
// 鼠标事件监听
canvas.addEventListener('mousedown', (e) => {
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY];
});
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);
// 触摸事件支持
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
});
canvas.addEventListener('touchend', (e) => {
e.preventDefault();
const mouseEvent = new MouseEvent('mouseup', {});
canvas.dispatchEvent(mouseEvent);
});
// 清空画布
clearBtn.addEventListener('click', function() {
if (confirm('确定要清空画布吗?')) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
localStorage.removeItem('canvasData');
updateThumbnails();
}
});
// 保存图像
saveBtn.addEventListener('click', function() {
const link = document.createElement('a');
link.download = 'canvas-drawing.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
// 保存Canvas状态
function saveCanvasState() {
localStorage.setItem('canvasData', canvas.toDataURL());
}
// 恢复Canvas状态
function restoreCanvas() {
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.src = localStorage.getItem('canvasData');
}
// 创建缩略图
function createThumbnail(index) {
const thumbnail = document.createElement('div');
thumbnail.className = 'thumbnail';
thumbnail.dataset.index = index;
const canvas = document.createElement('canvas');
canvas.width = 280;
canvas.height = 120;
thumbnail.appendChild(canvas);
// 从本地存储加载图像
const img = new Image();
img.onload = function() {
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
const savedData = localStorage.getItem(`canvasSnapshot_${index}`);
if (savedData) {
img.src = savedData;
}
thumbnail.addEventListener('click', function() {
// 恢复这个版本的画布
if (savedData) {
const restoreImg = new Image();
restoreImg.onload = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(restoreImg, 0, 0);
saveCanvasState();
};
restoreImg.src = savedData;
}
});
return thumbnail;
}
// 更新缩略图
function updateThumbnails() {
// 保存当前状态为快照
const timestamp = new Date().getTime();
localStorage.setItem(`canvasSnapshot_${timestamp}`, canvas.toDataURL());
// 保留最新的5个缩略图
const keys = Object.keys(localStorage)
.filter(key => key.startsWith('canvasSnapshot_'))
.sort()
.reverse()
.slice(0, 5);
thumbnailsContainer.innerHTML = '';
keys.forEach((key, index) => {
const thumbnail = createThumbnail(key.split('_')[1]);
thumbnailsContainer.appendChild(thumbnail);
});
}
// 面板拖动功能
let isDragging = false;
let startX, startWidth;
dragHandle.addEventListener('mousedown', function(e) {
isDragging = true;
startX = e.clientX;
startWidth = navPanel.offsetWidth;
document.body.style.cursor = 'col-resize';
e.preventDefault();
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
const newWidth = startWidth + (startX - e.clientX);
if (newWidth > 200 && newWidth < 500) {
navPanel.style.width = `${newWidth}px`;
}
});
document.addEventListener('mouseup', function() {
isDragging = false;
document.body.style.cursor = '';
});
// 切换面板显示/隐藏
togglePanelBtn.addEventListener('click', function() {
navPanel.classList.toggle('open');
});
// 初始化缩略图
updateThumbnails();
});
</script>
</body>
</html>
2176

被折叠的 条评论
为什么被折叠?



