<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">图片裁剪工具 · 按比例裁剪</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
background: #f2f4f8;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 16px;
}
.card {
background: white;
border-radius: 24px;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
padding: 24px 28px 32px;
max-width: 780px;
width: 100%;
transition: all 0.2s;
}
h1 {
font-size: 1.6rem;
font-weight: 600;
letter-spacing: -0.01em;
display: flex;
align-items: center;
gap: 8px;
color: #1e293b;
margin-bottom: 8px;
}
.sub {
color: #64748b;
font-size: 0.9rem;
margin-bottom: 20px;
border-left: 4px solid #3b82f6;
padding-left: 14px;
background: #f8fafc;
border-radius: 0 8px 8px 0;
line-height: 1.4;
}
.upload-area {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
margin-bottom: 20px;
background: #f9fafb;
padding: 12px 16px;
border-radius: 60px;
border: 1px dashed #cbd5e1;
}
.upload-btn {
background: #3b82f6;
color: white;
border: none;
padding: 10px 22px;
border-radius: 40px;
font-weight: 500;
font-size: 0.95rem;
cursor: pointer;
transition: 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.2);
}
.upload-btn:hover {
background: #2563eb;
transform: scale(1.02);
}
.upload-btn:active {
transform: scale(0.97);
}
.file-name {
color: #334155;
font-size: 0.95rem;
font-weight: 450;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
flex: 1;
}
.ratio-control {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px 18px;
margin-bottom: 24px;
background: #f1f5f9;
padding: 12px 18px;
border-radius: 40px;
}
.ratio-group {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.ratio-group label {
font-size: 0.9rem;
font-weight: 500;
color: #1e293b;
background: white;
padding: 4px 14px;
border-radius: 30px;
border: 1px solid #e2e8f0;
cursor: pointer;
transition: all 0.15s;
box-shadow: 0 1px 3px rgba(0,0,0,0.02);
}
.ratio-group input[type="radio"] {
display: none;
}
.ratio-group input[type="radio"]:checked + label {
background: #3b82f6;
color: white;
border-color: #3b82f6;
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
}
.custom-ratio {
display: flex;
align-items: center;
gap: 6px;
background: white;
padding: 2px 8px 2px 14px;
border-radius: 40px;
border: 1px solid #e2e8f0;
}
.custom-ratio input {
width: 52px;
padding: 6px 4px;
border: none;
background: transparent;
font-weight: 500;
font-size: 0.9rem;
text-align: center;
outline: none;
color: #0f172a;
}
.custom-ratio input:focus {
background: #f0f4ff;
border-radius: 20px;
}
.custom-ratio span {
color: #64748b;
font-weight: 400;
font-size: 1.1rem;
}
.canvas-wrapper {
position: relative;
background: #0f172a;
border-radius: 20px;
overflow: hidden;
margin-bottom: 18px;
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.05);
}
#imageCanvas {
display: block;
width: 100%;
height: auto;
max-height: 460px;
background: #1e293b;
object-fit: contain;
touch-action: none;
cursor: crosshair;
}
.action-bar {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-top: 6px;
}
.action-btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.btn {
background: white;
border: 1px solid #d1d9e6;
padding: 8px 20px;
border-radius: 40px;
font-weight: 500;
color: #1e293b;
font-size: 0.9rem;
cursor: pointer;
transition: 0.15s;
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.btn-primary {
background: #3b82f6;
border: 1px solid #3b82f6;
color: white;
box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3);
}
.btn-primary:hover {
background: #2563eb;
border-color: #2563eb;
}
.btn-primary:active {
transform: scale(0.96);
}
.btn:active {
transform: scale(0.96);
}
.btn:disabled {
opacity: 0.5;
pointer-events: none;
filter: grayscale(0.3);
}
.download-hint {
color: #475569;
font-size: 0.85rem;
background: #f1f5f9;
padding: 4px 16px;
border-radius: 30px;
white-space: nowrap;
}
.footer-note {
margin-top: 18px;
text-align: center;
font-size: 0.75rem;
color: #94a3b8;
}
@media (max-width: 500px) {
.card { padding: 16px; }
.upload-area { flex-direction: column; align-items: stretch; border-radius: 28px; }
.file-name { max-width: 100%; }
.ratio-control { flex-direction: column; align-items: stretch; gap: 8px; }
.action-bar { flex-direction: column; align-items: stretch; }
.action-btn-group { justify-content: center; }
.download-hint { text-align: center; }
}
</style>
</head>
<body>
<div class="card">
<h1>✂️ 比例裁剪</h1>
<div class="sub">拖拽鼠标 / 手指移动 · 自动保持宽高比</div>
<!-- 上传 & 比例 -->
<div class="upload-area">
<label for="imageUpload" class="upload-btn">📁 选择图片</label>
<input type="file" id="imageUpload" accept="image/png, image/jpeg, image/webp, image/bmp" style="display:none;">
<span class="file-name" id="fileName">未选择文件</span>
</div>
<div class="ratio-control">
<div class="ratio-group">
<input type="radio" name="ratio" id="ratioFree" value="free" checked>
<label for="ratioFree">🔄 自由</label>
<input type="radio" name="ratio" id="ratio1_1" value="1:1">
<label for="ratio1_1">1:1</label>
<input type="radio" name="ratio" id="ratio4_3" value="4:3">
<label for="ratio4_3">4:3</label>
<input type="radio" name="ratio" id="ratio16_9" value="16:9">
<label for="ratio16_9">16:9</label>
<input type="radio" name="ratio" id="ratio3_2" value="3:2">
<label for="ratio3_2">3:2</label>
</div>
<div class="custom-ratio">
<span>✏️</span>
<input type="number" id="ratioW" value="4" min="0.1" step="0.1" placeholder="宽">
<span>:</span>
<input type="number" id="ratioH" value="3" min="0.1" step="0.1" placeholder="高">
</div>
</div>
<!-- Canvas 容器 -->
<div class="canvas-wrapper">
<canvas id="imageCanvas" width="640" height="400"></canvas>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<div class="action-btn-group">
<button class="btn" id="resetBtn">↺ 重置</button>
<button class="btn btn-primary" id="cropBtn">✂️ 裁剪并下载</button>
</div>
<span class="download-hint">⬇️ 点击裁剪自动保存</span>
</div>
<div class="footer-note">拖拽选区 · 支持触屏操作</div>
</div>
<script>
(function() {
// DOM 元素
const canvas = document.getElementById('imageCanvas');
const ctx = canvas.getContext('2d');
const fileInput = document.getElementById('imageUpload');
const fileName = document.getElementById('fileName');
const resetBtn = document.getElementById('resetBtn');
const cropBtn = document.getElementById('cropBtn');
// 比例单选
const ratioRadios = document.querySelectorAll('input[name="ratio"]');
const ratioWInput = document.getElementById('ratioW');
const ratioHInput = document.getElementById('ratioH');
// 状态
let image = null; // 原始图片 (HTMLImageElement)
let imageLoaded = false;
// 选区 (归一化 0~1 相对于画布显示区域)
let sel = { x: 0.1, y: 0.1, w: 0.8, h: 0.8 };
// 拖拽状态
let isDragging = false;
let dragType = ''; // 'move' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'edge'
let dragStart = { x: 0, y: 0 };
let dragSel = { x: 0, y: 0, w: 0, h: 0 };
// 画布尺寸 (显示尺寸)
let canvasDisplayWidth = 0;
let canvasDisplayHeight = 0;
// 初次重置选区到合理位置
function resetSelection() {
if (!imageLoaded || !image) {
// 默认选区
sel.x = 0.05; sel.y = 0.05; sel.w = 0.9; sel.h = 0.9;
drawCanvas();
return;
}
// 根据图片比例调整初始选区 (占满 85% 区域)
const imgAspect = image.naturalWidth / image.naturalHeight;
const dispAspect = canvasDisplayWidth / canvasDisplayHeight;
let w, h;
if (imgAspect > dispAspect) {
w = 0.9;
h = w / (imgAspect / dispAspect);
} else {
h = 0.9;
w = h * (imgAspect / dispAspect);
}
// 居中
sel.x = (1 - w) / 2;
sel.y = (1 - h) / 2;
sel.w = Math.min(w, 0.95);
sel.h = Math.min(h, 0.95);
clampSelection();
drawCanvas();
}
// 将选区限制在画布内
function clampSelection() {
if (sel.x < 0) sel.x = 0;
if (sel.y < 0) sel.y = 0;
if (sel.x + sel.w > 1) sel.w = 1 - sel.x;
if (sel.y + sel.h > 1) sel.h = 1 - sel.y;
if (sel.w < 0.01) sel.w = 0.01;
if (sel.h < 0.01) sel.h = 0.01;
}
// 获取当前比例约束 (返回值: [wRatio, hRatio] 或 null 表示自由)
function getCurrentRatio() {
for (const radio of ratioRadios) {
if (radio.checked) {
const val = radio.value;
if (val === 'free') return null;
if (val === '1:1') return [1, 1];
if (val === '4:3') return [4, 3];
if (val === '16:9') return [16, 9];
if (val === '3:2') return [3, 2];
}
}
// 自定义 (如果自由未选中则读取自定义)
const w = parseFloat(ratioWInput.value);
const h = parseFloat(ratioHInput.value);
if (isNaN(w) || isNaN(h) || w <= 0 || h <= 0) return null;
return [w, h];
}
// 应用比例约束 (修改 sel 原地)
function applyRatioConstraint() {
const ratio = getCurrentRatio();
if (!ratio) return; // 自由模式
const [rw, rh] = ratio;
const currentAspect = sel.w / sel.h;
const targetAspect = rw / rh;
// 以中心为锚点调整宽/高
const cx = sel.x + sel.w / 2;
const cy = sel.y + sel.h / 2;
let newW, newH;
if (currentAspect > targetAspect) {
// 过宽: 缩小宽度
newW = sel.h * targetAspect;
newH = sel.h;
} else {
// 过高: 缩小高度
newH = sel.w / targetAspect;
newW = sel.w;
}
sel.w = Math.min(newW, 1);
sel.h = Math.min(newH, 1);
sel.x = Math.max(0, Math.min(1 - sel.w, cx - sel.w / 2));
sel.y = Math.max(0, Math.min(1 - sel.h, cy - sel.h / 2));
clampSelection();
}
// ---------- 绘图 ----------
function drawCanvas() {
const rect = canvas.getBoundingClientRect();
canvasDisplayWidth = rect.width;
canvasDisplayHeight = rect.height;
// 设置画布实际分辨率 (2倍缩放提升清晰度)
const dpr = window.devicePixelRatio || 1;
const drawW = canvasDisplayWidth * dpr;
const drawH = canvasDisplayHeight * dpr;
if (canvas.width !== drawW || canvas.height !== drawH) {
canvas.width = drawW;
canvas.height = drawH;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 背景 (暗色)
ctx.fillStyle = '#1e293b';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (!imageLoaded || !image) {
// 显示提示文字
ctx.fillStyle = '#94a3b8';
ctx.font = `${18 * dpr}px system-ui, sans-serif`;
ctx.textAlign = 'center';
ctx.fillText('请选择一张图片', canvas.width/2, canvas.height/2 - 10);
return;
}
// 绘制图片 (保持宽高比适应画布)
const imgW = image.naturalWidth;
const imgH = image.naturalHeight;
const cw = canvas.width, ch = canvas.height;
const scale = Math.min(cw / imgW, ch / imgH);
const offsetX = (cw - imgW * scale) / 2;
const offsetY = (ch - imgH * scale) / 2;
ctx.drawImage(image, offsetX, offsetY, imgW * scale, imgH * scale);
// 绘制遮罩 + 选区
// 选区坐标转换为 canvas 像素
const sx = offsetX + sel.x * (imgW * scale);
const sy = offsetY + sel.y * (imgH * scale);
const sw = sel.w * (imgW * scale);
const sh = sel.h * (imgH * scale);
// 遮罩 (半透明黑)
ctx.fillStyle = 'rgba(0,0,0,0.45)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 擦出选区 (使用 'destination-out' 需要复合模式,改用 clearRect 但需保留背景? 更简单:使用globalCompositeOperation)
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0,0,0,1)';
ctx.fillRect(sx, sy, sw, sh);
ctx.restore();
// 绘制亮色边框
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2 * dpr;
ctx.setLineDash([]);
ctx.strokeRect(sx, sy, sw, sh);
// 绘制四个角柄
const handleSize = 10 * dpr;
const handles = [
[sx, sy, 'nw'], [sx+sw, sy, 'ne'],
[sx, sy+sh, 'sw'], [sx+sw, sy+sh, 'se']
];
ctx.fillStyle = '#3b82f6';
ctx.strokeStyle = 'white';
ctx.lineWidth = 1.5 * dpr;
for (let [hx, hy] of handles) {
ctx.fillRect(hx - handleSize/2, hy - handleSize/2, handleSize, handleSize);
ctx.strokeRect(hx - handleSize/2, hy - handleSize/2, handleSize, handleSize);
}
// 如果图片加载完成,更新文件名字
}
// 重绘 & 尺寸自适应
function redraw() {
if (!canvasDisplayWidth || !canvasDisplayHeight) {
const rect = canvas.getBoundingClientRect();
canvasDisplayWidth = rect.width;
canvasDisplayHeight = rect.height;
}
drawCanvas();
}
// ---------- 图片加载 ----------
function loadImage(file) {
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
image = img;
imageLoaded = true;
fileName.textContent = file.name;
// 重置选区
resetSelection();
redraw();
};
img.onerror = () => {
alert('图片加载失败,请选择有效图片文件。');
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
loadImage(e.target.files[0]);
}
});
// ---------- 鼠标/触屏 交互 ----------
function getCanvasCoords(e) {
const rect = canvas.getBoundingClientRect();
let clientX, clientY;
if (e.touches) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
e.preventDefault();
} else {
clientX = e.clientX;
clientY = e.clientY;
}
return {
x: (clientX - rect.left) / rect.width,
y: (clientY - rect.top) / rect.height
};
}
function handlePointerDown(e) {
if (!imageLoaded) return;
const pos = getCanvasCoords(e);
const px = pos.x, py = pos.y;
// 检查是否在角柄 (像素判断需要转换)
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const offX = (canvas.width - image.naturalWidth * Math.min(canvas.width/image.naturalWidth, canvas.height/image.naturalHeight)) / 2;
const offY = (canvas.height - image.naturalHeight * Math.min(canvas.width/image.naturalWidth, canvas.height/image.naturalHeight)) / 2;
const imgScale = Math.min(canvas.width / image.naturalWidth, canvas.height / image.naturalHeight);
// 计算手柄位置 (像素)
const sx = offX + sel.x * (image.naturalWidth * imgScale);
const sy = offY + sel.y * (image.naturalHeight * imgScale);
const sw = sel.w * (image.naturalWidth * imgScale);
const sh = sel.h * (image.naturalHeight * imgScale);
const handleSize = 10 * (window.devicePixelRatio || 1);
const cx = (clientXFromEvent(e) - rect.left) * (canvas.width / rect.width);
const cy = (clientYFromEvent(e) - rect.top) * (canvas.height / rect.height);
function clientXFromEvent(ev) {
return ev.touches ? ev.touches[0].clientX : ev.clientX;
}
function clientYFromEvent(ev) {
return ev.touches ? ev.touches[0].clientY : ev.clientY;
}
// 检查角手柄 (按像素)
const corners = [
{ x: sx, y: sy, type: 'topLeft' },
{ x: sx+sw, y: sy, type: 'topRight' },
{ x: sx, y: sy+sh, type: 'bottomLeft' },
{ x: sx+sw, y: sy+sh, type: 'bottomRight' }
];
for (const corner of corners) {
if (Math.abs(cx - corner.x) < handleSize && Math.abs(cy - corner.y) < handleSize) {
isDragging = true;
dragType = corner.type;
dragStart = { x: px, y: py };
dragSel = { ...sel };
return;
}
}
// 检查是否在选区内 (移动)
if (px >= sel.x && px <= sel.x + sel.w && py >= sel.y && py <= sel.y + sel.h) {
isDragging = true;
dragType = 'move';
dragStart = { x: px, y: py };
dragSel = { ...sel };
return;
}
// 点击外部: 重新选区 (可选)
// 为了简化,点击外部不重置,但可做备用
}
function handlePointerMove(e) {
if (!isDragging || !imageLoaded) return;
e.preventDefault();
const pos = getCanvasCoords(e);
const dx = pos.x - dragStart.x;
const dy = pos.y - dragStart.y;
const newSel = { ...dragSel };
if (dragType === 'move') {
newSel.x = dragSel.x + dx;
newSel.y = dragSel.y + dy;
// 边界限制
if (newSel.x < 0) newSel.x = 0;
if (newSel.y < 0) newSel.y = 0;
if (newSel.x + newSel.w > 1) newSel.x = 1 - newSel.w;
if (newSel.y + newSel.h > 1) newSel.y = 1 - newSel.h;
sel = newSel;
clampSelection();
drawCanvas();
return;
}
// 角缩放
const ratio = getCurrentRatio();
let rw = ratio ? ratio[0] : null;
let rh = ratio ? ratio[1] : null;
const processCorner = (fixedX, fixedY, freeX, freeY) => {
let rawW, rawH;
if (freeX) {
rawW = dragSel.w + dx;
} else {
rawW = dragSel.w - dx;
}
if (freeY) {
rawH = dragSel.h + dy;
} else {
rawH = dragSel.h - dy;
}
if (rawW < 0.01) rawW = 0.01;
if (rawH < 0.01) rawH = 0.01;
let newW = rawW, newH = rawH;
if (rw && rh) {
// 锁定比例: 根据拖拽方向决定以宽为准还是高为准
const targetAspect = rw / rh;
// 尝试以宽为准
let tryW = rawW;
let tryH = rawW / targetAspect;
if (tryH > 0 && tryH < 1.5) {
newW = tryW; newH = tryH;
} else {
newH = rawH;
newW = rawH * targetAspect;
}
}
// 计算固定点
let newX = fixedX ? dragSel.x : (dragSel.x + dragSel.w - newW);
let newY = fixedY ? dragSel.y : (dragSel.y + dragSel.h - newH);
// 边界修正
if (newX < 0) { newX = 0; if (rw && rh) newW = Math.min(newW, 1); }
if (newY < 0) { newY = 0; if (rw && rh) newH = Math.min(newH, 1); }
if (newX + newW > 1) { newX = 1 - newW; }
if (newY + newH > 1) { newY = 1 - newH; }
sel.x = newX; sel.y = newY; sel.w = newW; sel.h = newH;
clampSelection();
};
switch (dragType) {
case 'topLeft': processCorner(true, true, false, false); break;
case 'topRight': processCorner(false, true, true, false); break;
case 'bottomLeft': processCorner(true, false, false, true); break;
case 'bottomRight': processCorner(false, false, true, true); break;
default: break;
}
drawCanvas();
}
function handlePointerUp(e) {
if (isDragging) {
isDragging = false;
dragType = '';
// 保证比例约束 (如果是自由则跳过)
applyRatioConstraint();
drawCanvas();
}
}
// 注册事件
canvas.addEventListener('mousedown', handlePointerDown);
window.addEventListener('mousemove', handlePointerMove);
window.addEventListener('mouseup', handlePointerUp);
canvas.addEventListener('touchstart', handlePointerDown, { passive: false });
window.addEventListener('touchmove', handlePointerMove, { passive: false });
window.addEventListener('touchend', handlePointerUp);
// 比例切换重绘
for (const radio of ratioRadios) {
radio.addEventListener('change', () => {
if (imageLoaded) {
applyRatioConstraint();
drawCanvas();
}
});
}
ratioWInput.addEventListener('input', () => {
if (imageLoaded) {
// 如果自由模式,不强制;但若自定义比例,需要检查
const ratio = getCurrentRatio();
if (ratio) {
applyRatioConstraint();
drawCanvas();
}
}
});
ratioHInput.addEventListener('input', () => {
if (imageLoaded) {
const ratio = getCurrentRatio();
if (ratio) {
applyRatioConstraint();
drawCanvas();
}
}
});
// 重置
resetBtn.addEventListener('click', () => {
if (imageLoaded) {
resetSelection();
applyRatioConstraint();
drawCanvas();
} else {
sel = { x: 0.05, y: 0.05, w: 0.9, h: 0.9 };
drawCanvas();
}
});
// 裁剪下载
cropBtn.addEventListener('click', () => {
if (!imageLoaded || !image) {
alert('请先加载一张图片');
return;
}
// 计算裁剪区域 (基于原始图片)
const imgW = image.naturalWidth;
const imgH = image.naturalHeight;
// 计算画布中图片的绘制区域 (同drawCanvas)
const cw = canvas.width, ch = canvas.height;
const scale = Math.min(cw / imgW, ch / imgH);
const offsetX = (cw - imgW * scale) / 2;
const offsetY = (ch - imgH * scale) / 2;
// 选区在原始图片上的像素坐标
const cropX = Math.round((sel.x * (imgW * scale)) / scale);
const cropY = Math.round((sel.y * (imgH * scale)) / scale);
const cropW = Math.round((sel.w * (imgW * scale)) / scale);
const cropH = Math.round((sel.h * (imgH * scale)) / scale);
// 边界保护
const safeX = Math.max(0, Math.min(cropX, imgW - 1));
const safeY = Math.max(0, Math.min(cropY, imgH - 1));
const safeW = Math.min(cropW, imgW - safeX);
const safeH = Math.min(cropH, imgH - safeY);
if (safeW < 2 || safeH < 2) {
alert('选区太小,请调整裁剪区域');
return;
}
// 创建临时canvas
const tempCanvas = document.createElement('canvas');
tempCanvas.width = safeW;
tempCanvas.height = safeH;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(image, safeX, safeY, safeW, safeH, 0, 0, safeW, safeH);
// 下载
const link = document.createElement('a');
link.download = 'cropped_image.png';
link.href = tempCanvas.toDataURL('image/png');
link.click();
});
// 窗口变化自适应
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (imageLoaded) redraw();
else drawCanvas();
}, 100);
});
// 初始绘制
drawCanvas();
// 如果有默认图片? 无
// 设置默认自定义比例为4:3
ratioWInput.value = 4;
ratioHInput.value = 3;
})();
</script>
</body>
</html>
功能与操作方法
您可以通过简单的拖拽和选择,快速实现图片的精确裁剪,其核心操作流程如下:

- 加载图片:点击“选择图片”按钮上传您需要裁剪的图片。
- 设置比例:您可以在上方选择预设比例(如1:1、4:3)或自定义宽高比,选区将自动遵循所选比例。
- 调整选区:在图片上拖拽鼠标或手指来移动选区,或拖拽选区的四个角来调整大小,选区会始终保持您设定的宽高比。
- 完成裁剪:调整满意后,点击“裁剪并下载”按钮,系统将按照选区范围生成并保存裁剪后的图片。
标签: 比例裁剪
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播