当前位置:   article > 正文

js原生上传图片、裁剪_js 上传图片 自定义裁剪

js 上传图片 自定义裁剪

HTML  

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>图片上传裁剪</title>
  7. <link rel="stylesheet" href="./index.css">
  8. </head>
  9. <body>
  10. <div class="container">
  11. <div class="upload">
  12. <input type="file" id="file" accept="image/*">
  13. <div class="clipImage">
  14. <img id="clipImage" src="">
  15. <img id="clipPathImg" src="">
  16. <div id="clip">
  17. <div class="lt"></div>
  18. <div class="rt"></div>
  19. <div class="rb"></div>
  20. <div class="lb"></div>
  21. </div>
  22. </div>
  23. </div>
  24. <div class="preview">
  25. <div class="preview-img">
  26. <span>浏览:</span>
  27. <img id="clipView" src="" alt="">
  28. <button id="uploadImage">上传</button>
  29. </div>
  30. </div>
  31. </div>
  32. <script src="./index.js"></script>
  33. <script>
  34. let uploadImage = document.getElementById('uploadImage');
  35. uploadImage.onclick = async function(){
  36. if(!curCanvas){
  37. alert('请先上传图片');
  38. return;
  39. }
  40. // 获取裁剪后的图片file对象
  41. let uploadFile = await getClipFile(curCanvas);
  42. console.log(uploadFile);
  43. }
  44. </script>
  45. </body>
  46. </html>

CSS  

  1. *{
  2. margin: 0;
  3. padding: 0;
  4. box-sizing: border-box;
  5. }
  6. .container{
  7. width: 1000px;
  8. display: flex;
  9. justify-content: center;
  10. margin: 0 auto;
  11. }
  12. .upload{
  13. width: 50%;
  14. }
  15. .clipImage{
  16. width: 100%;
  17. position: relative;
  18. }
  19. .clipImage img{
  20. width: 100%;
  21. top: 0;
  22. left: 0;
  23. display: block;
  24. }
  25. .clipImage::after{
  26. content: '';
  27. position: absolute;
  28. width: 100%;
  29. height: 100%;
  30. top: 0;
  31. left: 0;
  32. background: rgba(0, 0, 0, 0.5);
  33. z-index: 1;
  34. }
  35. .clipImage #clipPathImg{
  36. position: absolute;
  37. top: 0;
  38. left: 0;
  39. width: 100%;
  40. height: 100%;
  41. z-index: 2;
  42. -webkit-clip-path: var(--clipPath);
  43. clip-path: var(--clipPath);
  44. }
  45. /* 裁剪框 */
  46. #clip{
  47. position: absolute;
  48. top: 0;
  49. left: 0;
  50. width: var(--clipWidth);
  51. height: var(--clipHeight);
  52. left: var(--clipX);
  53. top: var(--clipY);
  54. z-index: 3;
  55. cursor: move;
  56. /* border: 2px solid red; */
  57. display: none;
  58. }
  59. #clip .lt{
  60. position: absolute;
  61. width: 10px;
  62. height: 10px;
  63. background: #fff;
  64. border: 1px solid #000;
  65. left: -5px;
  66. top: -5px;
  67. cursor: nw-resize;
  68. }
  69. #clip .rt{
  70. position: absolute;
  71. width: 10px;
  72. height: 10px;
  73. background: #fff;
  74. border: 1px solid #000;
  75. right: -5px;
  76. top: -5px;
  77. cursor: ne-resize;
  78. }
  79. #clip .lb{
  80. position: absolute;
  81. width: 10px;
  82. height: 10px;
  83. background: #fff;
  84. border: 1px solid #000;
  85. left: -5px;
  86. bottom: -5px;
  87. cursor: sw-resize;
  88. }
  89. #clip .rb{
  90. position: absolute;
  91. width: 10px;
  92. height: 10px;
  93. background: #fff;
  94. border: 1px solid #000;
  95. right: -5px;
  96. bottom: -5px;
  97. cursor: se-resize;
  98. }
  99. /* 浏览 */
  100. .preview{
  101. width: 100%;
  102. height: 100%;
  103. display: flex;
  104. justify-content: center;
  105. }

JS 

  1. /**
  2. * 上传图片并裁剪
  3. */
  4. // file选择框
  5. let file = document.getElementById('file')
  6. // 裁剪背景图片
  7. let clipImage = document.getElementById('clipImage')
  8. // 裁剪使用的图片
  9. let clipPathImg = document.getElementById('clipPathImg')
  10. // 裁剪框
  11. let clip = document.getElementById('clip');
  12. // 浏览裁剪图片
  13. let clipView = document.getElementById('clipView');
  14. // 裁剪框的尺寸占图片比例
  15. let clipSize = 0.7;
  16. // 图片当前宽高
  17. let clipImageWidth = 0;
  18. let clipImageHeight = 0;
  19. // 图片原始宽高
  20. let clipImageOriginWidth = 0;
  21. let clipImageOriginHeight = 0;
  22. // 缩放比例
  23. let scale = 0;
  24. // 当前canvas对象
  25. let curCanvas = null;
  26. // 监听上传状态 浏览图片
  27. file.addEventListener('change', function () {
  28. let file = this.files[0];
  29. let reader = new FileReader();
  30. reader.readAsDataURL(file);
  31. reader.onload = function () {
  32. // 读取到的图片数据
  33. clipImage.src = reader.result;
  34. // 裁剪使用的图片
  35. clipPathImg.src = reader.result;
  36. //获取当前图片的宽高
  37. getClipImageSize(reader.result).then((res) => {
  38. // 初始化
  39. init();
  40. })
  41. }
  42. })
  43. // 获取上传图片的当前尺寸和原始尺寸
  44. function getClipImageSize(dataurl){
  45. return new Promise((resolve) => {
  46. clipImage.onload = function () {
  47. resolve(clipImage);
  48. }
  49. }).then((data) => {
  50. // 获取图片当前宽高
  51. clipImageWidth = data.width;
  52. clipImageHeight = data.height;
  53. return new Promise((resolve) => {
  54. // 获取图片原始宽高
  55. let img = document.createElement('img');
  56. img.src = dataurl;
  57. img.onload = function () {
  58. resolve(img)
  59. }
  60. })
  61. }).then((data) => {
  62. // 获取图片原始宽高
  63. clipImageOriginWidth = data.width;
  64. clipImageOriginHeight = data.height;
  65. return Promise.resolve();
  66. })
  67. }
  68. // 初始化
  69. function init(){
  70. // 显示裁剪框
  71. clip.style.display = 'block';
  72. // 缩放比例
  73. scale = clipImageOriginWidth / clipImageWidth;
  74. // 设置裁剪框的宽高
  75. let clipWidth = Math.floor(clipImageWidth*clipSize);
  76. let clipHeight = Math.floor(clipImageHeight*clipSize);
  77. setClipSize(clipWidth, clipHeight);
  78. // 设置裁剪框的位置
  79. let x = Math.floor((clipImageWidth - clipWidth)/2);
  80. let y = Math.floor((clipImageHeight - clipHeight)/2);
  81. setClipPosition(x, y);
  82. }
  83. // 设置裁剪框的尺寸
  84. function setClipSize(clipWidth, clipHeight){
  85. clip.style.setProperty('--clipWidth', clipWidth + 'px');
  86. clip.style.setProperty('--clipHeight', clipHeight + 'px');
  87. }
  88. // 设置裁剪框的位置
  89. function setClipPosition(x, y){
  90. clip.style.setProperty('--clipX', x + 'px');
  91. clip.style.setProperty('--clipY', y + 'px');
  92. // 设置裁剪阴影
  93. setClipPath();
  94. }
  95. // 设置裁剪阴影
  96. function setClipPath(){
  97. let lt = clip.offsetLeft + 'px' + ' ' + clip.offsetTop + 'px';
  98. let rt = (clip.offsetLeft + clip.offsetWidth) + 'px' + ' ' + clip.offsetTop + 'px';
  99. let rb = (clip.offsetLeft + clip.offsetWidth) + 'px' + ' ' + (clip.offsetTop + clip.offsetHeight) + 'px';
  100. let lb = clip.offsetLeft + 'px' + ' ' + (clip.offsetTop + clip.offsetHeight) + 'px' ;
  101. clipPathImg.style.setProperty('--clipPath', `polygon(${lt}, ${rt}, ${rb}, ${lb})`);
  102. // 裁剪
  103. let dataUrl = clipImg(clip.offsetLeft * scale, clip.offsetTop * scale, clip.offsetWidth * scale, clip.offsetHeight * scale, clip.offsetWidth, clip.offsetHeight);
  104. // 显示裁剪后的图片
  105. clipView.src = dataUrl;
  106. }
  107. // 裁剪
  108. function clipImg(x,y,cutWidth,cutHeight,width,height,){
  109. let canvas = document.createElement('canvas');
  110. let ctx = canvas.getContext('2d');
  111. canvas.width = width;
  112. canvas.height = height;
  113. ctx.drawImage(clipImage, x, y, cutWidth, cutHeight, 0, 0, width, height);
  114. // 当前canvas对象
  115. curCanvas = canvas;
  116. // 返回裁剪后的图片
  117. return canvas.toDataURL();
  118. }
  119. /**
  120. * 裁剪框的拖拽
  121. */
  122. // 鼠标按下时的位置
  123. let x = 0;
  124. let y = 0;
  125. // 鼠标按下时裁剪框的位置
  126. let offsetX = 0;
  127. let offsetY = 0;
  128. // 偏移量
  129. let py = {};
  130. // 鼠标按下
  131. clip.onmousedown = function (e) {
  132. // 鼠标按下时的位置
  133. x = e.clientX;
  134. y = e.clientY;
  135. // 鼠标按下时裁剪框的位置
  136. offsetX = clip.offsetLeft;
  137. offsetY = clip.offsetTop;
  138. // 偏移量
  139. py = {
  140. x: e.clientX - offsetX,
  141. y: e.clientY - offsetY
  142. }
  143. // 鼠标移动
  144. window.onmousemove = function (e) {
  145. // 鼠标移动的距离
  146. let moveX = e.clientX - py.x;
  147. let moveY = e.clientY - py.y;
  148. // 裁剪框的位置
  149. let left = moveX;
  150. let top = moveY;
  151. // 裁剪框的位置限制
  152. if (left < 0) {
  153. left = 0;
  154. }
  155. if (top < 0) {
  156. top = 0;
  157. }
  158. if (left > clipImageWidth - clip.offsetWidth) {
  159. left = clipImageWidth - clip.offsetWidth;
  160. }
  161. if (top > clipImageHeight - clip.offsetHeight) {
  162. top = clipImageHeight - clip.offsetHeight;
  163. }
  164. // 设置裁剪框的位置
  165. setClipPosition(left, top);
  166. }
  167. // 鼠标松开
  168. window.onmouseup = function () {
  169. window.onmouseup = null;
  170. window.onmousemove = null;
  171. }
  172. }
  173. /**
  174. * 裁剪框的缩放
  175. */
  176. // 获取裁剪框的四个角
  177. let zoomDom = {
  178. lt: document.getElementsByClassName('lt'),
  179. rt: document.getElementsByClassName('rt'),
  180. rb: document.getElementsByClassName('rb'),
  181. lb: document.getElementsByClassName('lb')
  182. };
  183. // 鼠标按下时的位置
  184. let zoomInfo = {
  185. x: 0,
  186. y: 0,
  187. minWidth: 100,
  188. minHeight: 100
  189. }
  190. // 鼠标按下时裁剪框的位置
  191. for (let key in zoomDom) {
  192. zoomDom[key][0].onmousedown = function (e) {
  193. // 禁止冒泡
  194. e.stopPropagation();
  195. // 鼠标按下时的位置
  196. zoomInfo.x = e.clientX;
  197. zoomInfo.y = e.clientY;
  198. // 鼠标移动
  199. window.onmousemove = function (e) {
  200. // 鼠标移动的距离
  201. let moveX = e.clientX - zoomInfo.x;
  202. let moveY = e.clientY - zoomInfo.y;
  203. // 裁剪框的位置
  204. let left = 0;
  205. let top = 0;
  206. // 裁剪框的尺寸
  207. let width = 0;
  208. let height = 0;
  209. // 左上角
  210. if (key === 'lt') {
  211. // 裁剪框的位置
  212. left = clip.offsetLeft + moveX;
  213. top = clip.offsetTop + moveY;
  214. // 裁剪框的尺寸
  215. width = clip.offsetWidth - moveX;
  216. height = clip.offsetHeight - moveY;
  217. // 裁剪框的位置限制
  218. if (left < 0) {
  219. left = 0;
  220. // 原来宽度+原来left
  221. width = clip.offsetWidth + clip.offsetLeft;
  222. }
  223. if (top < 0) {
  224. top = 0;
  225. // 原来高度+原来top
  226. height = clip.offsetHeight + clip.offsetTop;
  227. }
  228. }
  229. // 右上角
  230. if (key === 'rt') {
  231. // 裁剪框的位置
  232. top = clip.offsetTop + moveY;
  233. left = clip.offsetLeft;
  234. // 裁剪框的尺寸
  235. width = clip.offsetWidth + moveX;
  236. height = clip.offsetHeight - moveY;
  237. // 裁剪框的位置限制
  238. if (top < 0) {
  239. top = 0;
  240. // 原来高度+原来top
  241. height = clip.offsetHeight + clip.offsetTop;
  242. }
  243. if(left + width > clipImageWidth){
  244. // 图片当前宽-原来left
  245. width = clipImageWidth - left;
  246. }
  247. }
  248. // 右下角
  249. if (key === 'rb') {
  250. // 裁剪框的尺寸
  251. top = clip.offsetTop;
  252. left = clip.offsetLeft;
  253. width = clip.offsetWidth + moveX;
  254. height = clip.offsetHeight + moveY;
  255. // 裁剪框的位置限制
  256. // 当前的left+当前的width>图片当前宽
  257. if(clip.offsetLeft + width > clipImageWidth){
  258. width = clipImageWidth - left;
  259. }
  260. // 当前的top+当前的height>图片当前宽
  261. if(clip.offsetTop + height > clipImageHeight){
  262. height = clipImageHeight - top;
  263. }
  264. }
  265. // 左下角
  266. if (key === 'lb') {
  267. // 裁剪框的位置
  268. left = clip.offsetLeft + moveX;
  269. top = clip.offsetTop;
  270. // 裁剪框的尺寸
  271. width = clip.offsetWidth - moveX;
  272. height = clip.offsetHeight + moveY;
  273. // 裁剪框的位置限制
  274. if (left < 0) {
  275. left = 0;
  276. width = clip.offsetWidth + clip.offsetLeft;
  277. }
  278. // 当前的top+当前的height>图片当前宽
  279. if(clip.offsetTop + height > clipImageHeight){
  280. height = clipImageHeight - top;
  281. }
  282. }
  283. // 裁剪框的限制
  284. ({width,height,left,top} = ClipWidthPosition(width, height, left, top))
  285. // 设置裁剪框的位置
  286. setClipPosition(left, top);
  287. // 设置裁剪框的尺寸
  288. setClipSize(width, height);
  289. // 记录上一次的位置
  290. zoomInfo.x = e.clientX;
  291. zoomInfo.y = e.clientY;
  292. }
  293. // 鼠标松开
  294. window.onmouseup = function () {
  295. window.onmouseup = null;
  296. window.onmousemove = null;
  297. }
  298. }
  299. }
  300. /**
  301. * 裁剪框的宽高限制以及位置限制
  302. *
  303. * @param {裁剪框宽度} w
  304. * @param {裁剪框的高度} h
  305. * @param {裁剪框的left} l
  306. * @param {裁剪框的top} r
  307. * @returns {宽度,高度,left,top}
  308. */
  309. function ClipWidthPosition(w, h,l,t){
  310. let width = w;
  311. let height = h;
  312. let left = l;
  313. let top = t;
  314. if (w < zoomInfo.minWidth) {
  315. width = zoomInfo.minWidth;
  316. left = clip.offsetLeft;
  317. }
  318. if (h < zoomInfo.minHeight) {
  319. height = zoomInfo.minHeight;
  320. top = clip.offsetTop;
  321. }
  322. return {width, height, left, top};
  323. }
  324. // 获取file对象
  325. async function getClipFile(canvas){
  326. const file = await new Promise((resolve) => {
  327. canvas.toBlob((blob) => {
  328. resolve(new File([blob], 'clip.png', {type: 'image/png'}));
  329. })
  330. })
  331. return file;
  332. }

效果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/565292
推荐阅读
相关标签
  

闽ICP备14008679号