最轻量的图片处理工具:一个可以方便地添加文字和裁剪图片的.html

点击Chrome工具栏上的截图—区域截图—编辑,就能写字,画箭头、框、线,缩放。

它不能改文字大小和字体。我这个可以。多一个width: 1em,文字就竖着写了:玩法很多。添加后的文字可用鼠标拖拽,双击则隐藏。

还能裁剪图片。

t50%

HTML+JavaScript:

<html><head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>ImgTool</title><style>
* {
  margin: 0; padding: 0;
  border: none; box-sizing: border-box; /* 设置 后,元素的 width和 height包含border和padding */
  font: 12pt sans-serif;
}
body {
  display: flex; /* 设置为弹性布局容器 */
  justify-content: flex-start; /* 主轴从起始位置排列 */
  align-items: stretch; /* 交叉轴拉伸填满整个容器高度 */
  overflow: hidden;
  background: #333;
}
.left-panel {
  width: 19em;
  padding: 1em;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.8em;
  background: #eed;
}
button {
  width: 9em;
  padding: 0.5em 1em;
  background: #370; color: white;
  border-radius: 6px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);
  transition: all 0.3s ease;
}
button:hover {
  font-weight: bold;
  transform: translateY(-1px);
  box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}
#file { display: none; }
textarea { line-height: 130%; padding:0.2em; border: solid silver 1px; }
.right-panel {
  flex: 1; /* 自动填满剩余空间 */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
img {
  max-width: 100%; max-height: 100%;
  border: none;
  user-select: none;
}
/* p和div都是独占一行的块元素。p的margin-top/bottom默认不为0 */
.draggable-text {
  position: absolute;
  cursor: move;
  user-select: none;
}
.draggable-text:active { cursor: none; }
#crop {
  display: none;
  position: absolute;
  border: 1px dotted red;
  background: transparent;
  cursor: move;
}
.resize-handle {
  position: absolute;
  width: 1em; height: 1em;
  background: transparent;
  border: 1px dotted red;
}
.resize-nw { top: -1px; left: -1px; cursor: nw-resize; }
.resize-ne { top: -1px; right: -1px; cursor: ne-resize; }
.resize-sw { bottom: -1px; left: -1px; cursor: sw-resize; }
.resize-se { bottom: -1px; right: -1px; cursor: se-resize; }
</style></head><body>
<div class="left-panel">
  <input type="file" id="file" accept="image/*">
  <button onclick="file.click()">打开图片文件</button>
  <hr/><hr/>
  <textarea id="ta_css" spellcheck="false" rows=8 cols=25>left:600px; top:100px;&#13;&#10;color: red;&#13;&#10;font: 16pt sans;&#13;&#10;</textarea>
  <textarea id="ta_str" spellcheck="false" rows=3 cols=25 placeholder="输入文字"></textarea>
  <button onclick="addText()">添加文字</button>
  <hr/><hr/>
  <button onclick="crop.style.display = 'none'">隐藏裁剪区</button>
  <button onclick="crop.style.display = 'block'">显示裁剪区</button>
  <button onclick="cropImg()">裁剪图片</button>
</div>
<div class="right-panel"><img id="img"></div>
<div id="crop">
  <div class="resize-handle resize-nw"></div>
  <div class="resize-handle resize-ne"></div>
  <div class="resize-handle resize-sw"></div>
  <div class="resize-handle resize-se"></div>
</div>
<script>
file.addEventListener('change', f=>{
  let reader = new FileReader()
  reader.onload = e=>{ img.onload = initCrop; img.src = e.target.result }
  if ((f = f.target.files).length) reader.readAsDataURL(f[0])
})

let PX = 'px'
let d = document
let draggingEle, mouseX, mouseY
let resizing, resizeDir, cropX, cropY, cropR, cropB

function setCropPosition () {
  let s = crop.style
  s.left = cropX + PX; s.top = cropY + PX
  // 设置.right和.bottom不行
  s.width = (cropR - cropX) + PX; s.height = (cropB - cropY) + PX
}

function initCrop () {
  let w = img.width / 2, h = img.height / 2
  let rc = img.getBoundingClientRect()
  cropR = (cropX = rc.x + w / 2) + w; cropB = (cropY = rc.y + h / 2) + h
  setCropPosition()
  crop.style.display = 'block'
}

function mouseDown (e) {
  mouseX = e.clientX; mouseY = e.clientY
  draggingEle = e.target
  let cl = e.target.classList
  if (cl[0] === 'resize-handle') { resizing = true; resizeDir = cl[1].replace('resize-', '') }
  e.preventDefault()
}

crop.addEventListener('mousedown', mouseDown)

d.addEventListener('mouseup', ()=>{ draggingEle = undefined; resizing = false; resizeDir = '' })

d.addEventListener('mousemove', e=>{
  if (!draggingEle) return
  let dx = e.clientX - mouseX, dy = e.clientY - mouseY
  mouseX = e.clientX; mouseY = e.clientY
  if (e.target.tagName === 'P') {
    let rc = draggingEle.getBoundingClientRect()
    draggingEle.style.left = rc.x + dx + PX
    draggingEle.style.top = rc.y + dy + PX
    return
  }
  switch(resizeDir) {
  case 'nw': cropX += dx; cropY += dy; break;
  case 'ne': cropR += dx; cropY += dy; break;
  case 'sw': cropX += dx; cropB += dy; break;
  case 'se': cropR += dx; cropB += dy; break;
  default: cropX += dx; cropY += dy; cropR += dx; cropB += dy;
  }
  let rc = img.getBoundingClientRect()
  if (cropX < rc.x) cropX = rc.x
  if (cropY < rc.y) cropY = rc.y
  if (cropR > rc.right) cropR = rc.right
  if (cropB > rc.bottom) cropB = rc.bottom
  setCropPosition()
})

function addText () {
  let e = d.createElement('p')
  e.style.cssText = ta_css.value
  e.textContent = ta_str.value ? ta_str.value : '双击鼠标左键隐藏'
  e.classList.add('draggable-text')
  e.addEventListener('mousedown', mouseDown)
  e.addEventListener('dblclick', ev=>{ ev.target.style.display = 'none' })
  d.body.appendChild(e)
}

// 防止拖拽时选中文本
d.addEventListener('selectstart', e=>{ if (draggingEle) e.preventDefault() })

function cropImg () {
  let rc = img.getBoundingClientRect()
  let x = cropX - rc.x, y = cropY - rc.y,
      w = cropR - cropX, h = cropB - cropY
  let s = img.naturalWidth / rc.width
  x *= s; y *= s; w *= s; h *= s;
  let canvas = d.createElement('canvas')
  canvas.width = w; canvas.height = h
  let ctx = canvas.getContext('2d')
  ctx.drawImage(img, x, y, w, h, 0, 0, w, h)
  img.src = canvas.toDataURL('image/png')
  canvas = null
}
</script></body></html>
View Code

Python PIL缩放旋转:

from PIL import Image as Img
from os import path
import sys

if len(sys.argv) < 3: exit()
name = sys.argv[1]
try: img = Img.open(name)
except FileNotFoundError as e: print(e); exit()
for a in sys.argv[2:]:
  i = a.find('%')
  if i != -1:
    w,h = img.size
    scale = float(sys.argv[2][:i]) / 100
    size = (int(w * scale), int(h * scale))
    img = img.resize(size, Img.LANCZOS)
    w,h = img.size
  else: img = img.rotate(int(a))

root,ext = path.splitext(name)
img.save(root + '-'.join(sys.argv[2:]) + ext, compress_level=9)
View Code

反正要装Python和PIL,所以我这可能是最轻量的图片处理工具。

 

posted @ 2025-11-23 13:12  华容道专家  阅读(4)  评论(0)    收藏  举报