427 lines
12 KiB
HTML
427 lines
12 KiB
HTML
<!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;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
background: white;
|
|
border-radius: 20px;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
padding: 30px;
|
|
max-width: 900px;
|
|
width: 100%;
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
color: #333;
|
|
margin-bottom: 30px;
|
|
font-size: 28px;
|
|
}
|
|
|
|
.upload-area {
|
|
border: 3px dashed #667eea;
|
|
border-radius: 15px;
|
|
padding: 40px 20px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
margin-bottom: 20px;
|
|
background: #f8f9ff;
|
|
}
|
|
|
|
.upload-area:hover {
|
|
border-color: #764ba2;
|
|
background: #f0f1ff;
|
|
}
|
|
|
|
.upload-area.dragover {
|
|
border-color: #764ba2;
|
|
background: #e8e9ff;
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.upload-icon {
|
|
font-size: 48px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.upload-text {
|
|
color: #666;
|
|
font-size: 16px;
|
|
}
|
|
|
|
#fileInput {
|
|
display: none;
|
|
}
|
|
|
|
.controls {
|
|
margin: 20px 0;
|
|
display: none;
|
|
}
|
|
|
|
.control-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
color: #333;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.slider-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
|
|
input[type="range"] {
|
|
flex: 1;
|
|
height: 8px;
|
|
border-radius: 5px;
|
|
background: #e0e0e0;
|
|
outline: none;
|
|
-webkit-appearance: none;
|
|
}
|
|
|
|
input[type="range"]::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
background: #667eea;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
input[type="range"]::-webkit-slider-thumb:hover {
|
|
background: #764ba2;
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
input[type="range"]::-moz-range-thumb {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 50%;
|
|
background: #667eea;
|
|
cursor: pointer;
|
|
border: none;
|
|
}
|
|
|
|
.value-display {
|
|
min-width: 40px;
|
|
text-align: center;
|
|
font-weight: 600;
|
|
color: #667eea;
|
|
}
|
|
|
|
.canvas-container {
|
|
display: none;
|
|
margin: 20px 0;
|
|
text-align: center;
|
|
}
|
|
|
|
canvas {
|
|
max-width: 100%;
|
|
height: auto;
|
|
border-radius: 10px;
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.buttons {
|
|
display: none;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
button {
|
|
padding: 12px 30px;
|
|
border: none;
|
|
border-radius: 25px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
color: white;
|
|
}
|
|
|
|
.btn-download {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
|
|
.btn-download:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.btn-reset {
|
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
}
|
|
|
|
.btn-reset:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(245, 87, 108, 0.4);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
padding: 20px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 24px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.upload-area {
|
|
padding: 30px 15px;
|
|
}
|
|
|
|
.upload-icon {
|
|
font-size: 36px;
|
|
}
|
|
|
|
.upload-text {
|
|
font-size: 14px;
|
|
}
|
|
|
|
button {
|
|
padding: 10px 20px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
body {
|
|
padding: 10px;
|
|
}
|
|
|
|
.container {
|
|
padding: 15px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.buttons {
|
|
flex-direction: column;
|
|
}
|
|
|
|
button {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🎨 图片像素化工具</h1>
|
|
|
|
<div class="upload-area" id="uploadArea">
|
|
<div class="upload-icon">📁</div>
|
|
<div class="upload-text">点击或拖拽图片到这里</div>
|
|
<input type="file" id="fileInput" accept="image/*">
|
|
</div>
|
|
|
|
<div class="controls" id="controls">
|
|
<div class="control-group">
|
|
<label for="pixelSize">像素化程度</label>
|
|
<div class="slider-container">
|
|
<input type="range" id="pixelSize" min="1" max="50" value="10">
|
|
<span class="value-display" id="pixelValue">10</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="canvas-container" id="canvasContainer">
|
|
<canvas id="canvas"></canvas>
|
|
</div>
|
|
|
|
<div class="buttons" id="buttons">
|
|
<button class="btn-download" id="downloadBtn">下载图片</button>
|
|
<button class="btn-reset" id="resetBtn">重新上传</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
const fileInput = document.getElementById('fileInput');
|
|
const controls = document.getElementById('controls');
|
|
const canvasContainer = document.getElementById('canvasContainer');
|
|
const canvas = document.getElementById('canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const pixelSizeInput = document.getElementById('pixelSize');
|
|
const pixelValue = document.getElementById('pixelValue');
|
|
const buttons = document.getElementById('buttons');
|
|
const downloadBtn = document.getElementById('downloadBtn');
|
|
const resetBtn = document.getElementById('resetBtn');
|
|
|
|
let originalImage = null;
|
|
|
|
// 点击上传区域
|
|
uploadArea.addEventListener('click', () => {
|
|
fileInput.click();
|
|
});
|
|
|
|
// 文件选择
|
|
fileInput.addEventListener('change', (e) => {
|
|
const file = e.target.files[0];
|
|
if (file && file.type.startsWith('image/')) {
|
|
loadImage(file);
|
|
}
|
|
});
|
|
|
|
// 拖拽上传
|
|
uploadArea.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.classList.add('dragover');
|
|
});
|
|
|
|
uploadArea.addEventListener('dragleave', () => {
|
|
uploadArea.classList.remove('dragover');
|
|
});
|
|
|
|
uploadArea.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.classList.remove('dragover');
|
|
const file = e.dataTransfer.files[0];
|
|
if (file && file.type.startsWith('image/')) {
|
|
loadImage(file);
|
|
}
|
|
});
|
|
|
|
// 加载图片
|
|
function loadImage(file) {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const img = new Image();
|
|
img.onload = () => {
|
|
originalImage = img;
|
|
initCanvas();
|
|
pixelateImage();
|
|
showControls();
|
|
};
|
|
img.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
|
|
// 初始化画布
|
|
function initCanvas() {
|
|
const maxWidth = 800;
|
|
let width = originalImage.width;
|
|
let height = originalImage.height;
|
|
|
|
if (width > maxWidth) {
|
|
height = (maxWidth / width) * height;
|
|
width = maxWidth;
|
|
}
|
|
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
}
|
|
|
|
// 像素化图片
|
|
function pixelateImage() {
|
|
const pixelSize = parseInt(pixelSizeInput.value);
|
|
|
|
// 清空画布
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// 绘制原图
|
|
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height);
|
|
|
|
// 获取图像数据
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
const data = imageData.data;
|
|
|
|
// 像素化处理
|
|
for (let y = 0; y < canvas.height; y += pixelSize) {
|
|
for (let x = 0; x < canvas.width; x += pixelSize) {
|
|
// 计算区块的平均颜色
|
|
let r = 0, g = 0, b = 0, a = 0, count = 0;
|
|
|
|
for (let dy = 0; dy < pixelSize && y + dy < canvas.height; dy++) {
|
|
for (let dx = 0; dx < pixelSize && x + dx < canvas.width; dx++) {
|
|
const index = ((y + dy) * canvas.width + (x + dx)) * 4;
|
|
r += data[index];
|
|
g += data[index + 1];
|
|
b += data[index + 2];
|
|
a += data[index + 3];
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// 计算平均值
|
|
r = Math.floor(r / count);
|
|
g = Math.floor(g / count);
|
|
b = Math.floor(b / count);
|
|
a = Math.floor(a / count);
|
|
|
|
// 填充像素块
|
|
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
|
|
ctx.fillRect(x, y, pixelSize, pixelSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 显示控制面板
|
|
function showControls() {
|
|
controls.style.display = 'block';
|
|
canvasContainer.style.display = 'block';
|
|
buttons.style.display = 'flex';
|
|
}
|
|
|
|
// 像素大小改变
|
|
pixelSizeInput.addEventListener('input', (e) => {
|
|
pixelValue.textContent = e.target.value;
|
|
pixelateImage();
|
|
});
|
|
|
|
// 下载图片
|
|
downloadBtn.addEventListener('click', () => {
|
|
const link = document.createElement('a');
|
|
link.download = `pixelated-image-${Date.now()}.png`;
|
|
link.href = canvas.toDataURL('image/png');
|
|
link.click();
|
|
});
|
|
|
|
// 重置
|
|
resetBtn.addEventListener('click', () => {
|
|
originalImage = null;
|
|
fileInput.value = '';
|
|
controls.style.display = 'none';
|
|
canvasContainer.style.display = 'none';
|
|
buttons.style.display = 'none';
|
|
pixelSizeInput.value = 10;
|
|
pixelValue.textContent = '10';
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|