
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0" /> |
| <title>圆形裁剪</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| body { |
| padding: 20px; |
| } |
| img { |
| width: 500px; |
| } |
| img, |
| canvas { |
| outline: 1px solid #999; |
| } |
| </style> |
| </head> |
| <body> |
| <img src="./test.jpg" class="origin" alt="" /> |
| <button type="button" class="btn-crop">圆形裁剪</button> |
| <img class="clip" alt="" /> |
| |
| <script> |
| const origin = document.querySelector('.origin'); |
| const btnCrop = document.querySelector('.btn-crop'); |
| const clip = document.querySelector('.clip'); |
| |
| const canvas = document.createElement('canvas'); |
| const context = canvas.getContext('2d'); |
| const width = origin.offsetWidth; |
| const height = origin.offsetHeight; |
| canvas.width = width * devicePixelRatio; |
| canvas.height = width * devicePixelRatio; |
| canvas.style.width = `${100}px`; |
| canvas.style.height = `${100}px`; |
| clip.style.width = `${100}px`; |
| clip.style.height = `${100}px`; |
| |
| const scalex = origin.naturalWidth / width; |
| const scaley = origin.naturalHeight / height; |
| |
| context.drawImage(origin, 0, 0, width, height); |
| |
| context.beginPath(); |
| context.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, 2 * Math.PI); |
| context.clearRect(0, 0, width, height); |
| context.clip(); |
| |
| let mask = null; |
| origin.addEventListener('mouseenter', () => { |
| mask = document.createElement('div'); |
| const { left, top } = origin.getBoundingClientRect(); |
| mask.style.cssText = ` |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: ${canvas.offsetWidth}px; |
| height: ${canvas.offsetHeight}px; |
| border-radius: 50%; |
| background-color: rgba(0, 0, 0, 0.5); |
| translate:${left}px ${top}px; |
| will-change: translate; |
| z-index: 999; |
| pointer-events: none; |
| `; |
| document.body.append(mask); |
| }); |
| origin.addEventListener('mouseleave', () => { |
| if (mask) { |
| document.body.removeChild(mask); |
| } |
| }) |
| |
| window.onmousemove = (e) => { |
| |
| let x = e.clientX - origin.offsetLeft - canvas.offsetWidth / 2; |
| let y = e.clientY - origin.offsetTop - canvas.offsetHeight / 2; |
| |
| x = Math.max(x, 0); |
| y = Math.max(y, 0); |
| x = Math.min(x, origin.offsetWidth - origin.offsetLeft - canvas.offsetWidth / 2); |
| y = Math.min(y, origin.offsetHeight - origin.offsetTop - canvas.offsetHeight / 2); |
| |
| if (mask) { |
| |
| mask.style.translate = `${e.clientX - canvas.offsetWidth / 2}px ${ |
| e.clientY - canvas.offsetHeight / 2 |
| }px`; |
| } |
| |
| context.clearRect(0, 0, canvas.width, canvas.height); |
| context.drawImage( |
| origin, |
| x * scalex, |
| y * scaley, |
| canvas.offsetWidth * scalex, |
| canvas.offsetHeight * scaley, |
| 0, |
| 0, |
| canvas.offsetWidth * scalex * devicePixelRatio, |
| canvas.offsetHeight * scaley * devicePixelRatio |
| ); |
| }; |
| |
| origin.addEventListener('click', () => { |
| |
| canvas.toBlob( |
| (blob) => { |
| const url = URL.createObjectURL(blob); |
| clip.src = url; |
| |
| |
| }, |
| 'image/jpeg', |
| 1 |
| ); |
| }); |
| |
| document.body.append(canvas); |
| </script> |
| </body> |
| </html> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!