hookehuyr

fix 优化图片旋转效果

1 <!-- 1 <!--
2 * @Date: 2025-01-22 11:40:12 2 * @Date: 2025-01-22 11:40:12
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-03-04 18:06:43 4 + * @LastEditTime: 2025-03-05 10:39:58
5 * @FilePath: /map-demo/src/views/mapCutter.vue 5 * @FilePath: /map-demo/src/views/mapCutter.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -165,6 +165,10 @@ const log_lnglat = ref('') // 获取当前地址经纬度 ...@@ -165,6 +165,10 @@ const log_lnglat = ref('') // 获取当前地址经纬度
165 165
166 const fileInput = ref(null); // 绑定隐藏的 input 元素 166 const fileInput = ref(null); // 绑定隐藏的 input 元素
167 167
168 +const imageCorners = ref(null); // 存储图片的四个角的经纬度
169 +const originalBounds = ref(null); // 存储原始的图片边界
170 +const originalAspectRatio = ref(null); // 存储原始的图片宽高比
171 +
168 // 触发文件选择 172 // 触发文件选择
169 const triggerFileInput = () => { 173 const triggerFileInput = () => {
170 fileInput.value.click(); 174 fileInput.value.click();
...@@ -308,6 +312,7 @@ async function addImageToMap(url) { ...@@ -308,6 +312,7 @@ async function addImageToMap(url) {
308 const imgWidth = img.width; 312 const imgWidth = img.width;
309 const imgHeight = img.height; 313 const imgHeight = img.height;
310 const aspectRatio = Number((imgWidth / imgHeight).toFixed(3)); // 图片宽高比 314 const aspectRatio = Number((imgWidth / imgHeight).toFixed(3)); // 图片宽高比
315 + originalAspectRatio.value = aspectRatio; // 存储原始宽高比
311 316
312 // 获取左下角经纬度 317 // 获取左下角经纬度
313 const [lng1, lat1] = map_left_bottom_range.value; 318 const [lng1, lat1] = map_left_bottom_range.value;
...@@ -344,6 +349,8 @@ async function addImageToMap(url) { ...@@ -344,6 +349,8 @@ async function addImageToMap(url) {
344 349
345 // TAG: 设置图片范围 350 // TAG: 设置图片范围
346 bounds.value = new AMap.Bounds(map_left_bottom_range.value, map_right_top_range.value); // 设置图片范围 左下角 (西南) -> 右上角 (东北) 351 bounds.value = new AMap.Bounds(map_left_bottom_range.value, map_right_top_range.value); // 设置图片范围 左下角 (西南) -> 右上角 (东北)
352 + originalBounds.value = new AMap.Bounds(map_left_bottom_range.value, map_right_top_range.value); // 存储原始边界
353 +
347 imageLayer.value = new AMap.ImageLayer({ 354 imageLayer.value = new AMap.ImageLayer({
348 url: url, 355 url: url,
349 bounds: bounds.value, 356 bounds: bounds.value,
...@@ -354,7 +361,6 @@ async function addImageToMap(url) { ...@@ -354,7 +361,6 @@ async function addImageToMap(url) {
354 map.value.add(imageLayer.value); 361 map.value.add(imageLayer.value);
355 } 362 }
356 363
357 -
358 function cutTiles() { 364 function cutTiles() {
359 if (!imageURL.value) { 365 if (!imageURL.value) {
360 alert("请先上传图片"); 366 alert("请先上传图片");
...@@ -413,44 +419,66 @@ const imageRotation = ref(0); ...@@ -413,44 +419,66 @@ const imageRotation = ref(0);
413 419
414 // 修改 rotateMap 方法,只旋转图片 420 // 修改 rotateMap 方法,只旋转图片
415 const rotateMap = (deltaAngle) => { 421 const rotateMap = (deltaAngle) => {
416 - if (!imageLayer.value || !imageURL.value) return; 422 + if (!imageLayer.value || !imageURL.value || !originalBounds.value || !originalAspectRatio.value) return;
417 423
418 imageRotation.value = (imageRotation.value + deltaAngle) % 360; 424 imageRotation.value = (imageRotation.value + deltaAngle) % 360;
419 - // console.log(`图片旋转: ${imageRotation.value}°`);
420 425
421 const img = new Image(); 426 const img = new Image();
422 img.src = imageURL.value; 427 img.src = imageURL.value;
423 428
424 img.onload = () => { 429 img.onload = () => {
425 const canvas = document.createElement('canvas'); 430 const canvas = document.createElement('canvas');
426 - const ctx = canvas.getContext('2d'); 431 + const ctx = canvas.getContext('2d', { alpha: true });
432 +
433 + // 提高Canvas分辨率
434 + const scaleFactor = 2;
427 435
428 // 计算旋转后的画布大小 436 // 计算旋转后的画布大小
429 const angleRad = (imageRotation.value * Math.PI) / 180; 437 const angleRad = (imageRotation.value * Math.PI) / 180;
430 const cosAngle = Math.abs(Math.cos(angleRad)); 438 const cosAngle = Math.abs(Math.cos(angleRad));
431 const sinAngle = Math.abs(Math.sin(angleRad)); 439 const sinAngle = Math.abs(Math.sin(angleRad));
432 440
433 - // 计算旋转后需要的画布尺寸 441 + // 使用更精确的计算方式
434 - const newWidth = img.width * cosAngle + img.height * sinAngle; 442 + const newWidth = Math.ceil((img.width * cosAngle + img.height * sinAngle) * scaleFactor);
435 - const newHeight = img.width * sinAngle + img.height * cosAngle; 443 + const newHeight = Math.ceil((img.height * cosAngle + img.width * sinAngle) * scaleFactor);
436 444
437 - // 设置画布大小为旋转后的尺寸 445 + // 设置高分辨率画布
438 canvas.width = newWidth; 446 canvas.width = newWidth;
439 canvas.height = newHeight; 447 canvas.height = newHeight;
448 + canvas.style.width = `${newWidth / scaleFactor}px`;
449 + canvas.style.height = `${newHeight / scaleFactor}px`;
450 +
451 + // 清除画布背景
452 + ctx.clearRect(0, 0, newWidth, newHeight);
453 +
454 + // 设置更高质量的图像平滑
455 + ctx.imageSmoothingEnabled = true;
456 + ctx.imageSmoothingQuality = 'high';
457 +
458 + // 应用分辨率缩放
459 + ctx.scale(scaleFactor, scaleFactor);
440 460
441 // 移动到画布中心点 461 // 移动到画布中心点
442 - ctx.translate(newWidth / 2, newHeight / 2); 462 + ctx.translate(newWidth / (2 * scaleFactor), newHeight / (2 * scaleFactor));
443 - // 旋转画布 463 +
464 + // 使用精确的旋转角度
444 ctx.rotate(angleRad); 465 ctx.rotate(angleRad);
445 - // 绘制图片,从中心点开始绘制
446 - ctx.drawImage(img, -img.width / 2, -img.height / 2);
447 466
448 - // 获取旋转后的图片 URL 467 + // 使用亚像素定位进行绘制
449 - const rotatedImageURL = canvas.toDataURL(); 468 + ctx.drawImage(
469 + img,
470 + -img.width / 2,
471 + -img.height / 2,
472 + img.width,
473 + img.height
474 + );
450 475
451 - // 重新计算边界框以适应旋转后的图片 476 + // 获取高质量的旋转后图片URL
452 - const sw = bounds.value.getSouthWest(); 477 + const rotatedImageURL = canvas.toDataURL('image/png', 1.0);
453 - const ne = bounds.value.getNorthEast(); 478 +
479 + // 基于原始边界计算旋转后的边界
480 + const sw = originalBounds.value.getSouthWest();
481 + const ne = originalBounds.value.getNorthEast();
454 const centerLng = (sw.lng + ne.lng) / 2; 482 const centerLng = (sw.lng + ne.lng) / 2;
455 const centerLat = (sw.lat + ne.lat) / 2; 483 const centerLat = (sw.lat + ne.lat) / 2;
456 484
...@@ -477,16 +505,36 @@ const rotateMap = (deltaAngle) => { ...@@ -477,16 +505,36 @@ const rotateMap = (deltaAngle) => {
477 const minLat = Math.min(topLeft.lat, topRight.lat, bottomLeft.lat, bottomRight.lat); 505 const minLat = Math.min(topLeft.lat, topRight.lat, bottomLeft.lat, bottomRight.lat);
478 const maxLat = Math.max(topLeft.lat, topRight.lat, bottomLeft.lat, bottomRight.lat); 506 const maxLat = Math.max(topLeft.lat, topRight.lat, bottomLeft.lat, bottomRight.lat);
479 507
480 - const newBounds = new AMap.Bounds( 508 + // 计算旋转后的图片宽度和高度
481 - [minLng, minLat], 509 + const rotatedWidth = maxLng - minLng;
482 - [maxLng, maxLat] 510 + const rotatedHeight = maxLat - minLat;
483 - ); 511 +
512 + // 以左下角为基准,计算新的右上角坐标
513 + let newNeLng = bottomLeft.lng + rotatedWidth;
514 + let newNeLat = bottomLeft.lat + rotatedHeight;
515 +
516 + // 调整新的右上角坐标,确保图片完全显示在可视范围内
517 + if (newNeLng > ne.lng) {
518 + newNeLng = ne.lng;
519 + }
520 + if (newNeLat > ne.lat) {
521 + newNeLat = ne.lat;
522 + }
484 523
485 // 更新图层 524 // 更新图层
486 if (imageLayer.value) { 525 if (imageLayer.value) {
487 map.value.remove(imageLayer.value); 526 map.value.remove(imageLayer.value);
488 } 527 }
489 528
529 + // 使用计算出的最小和最大经纬度创建新的边界,确保图片完整显示
530 + let newBounds = new AMap.Bounds(
531 + [minLng, minLat],
532 + [maxLng, maxLat]
533 + );
534 +
535 + // 更新当前边界值
536 + bounds.value = newBounds;
537 +
490 imageLayer.value = new AMap.ImageLayer({ 538 imageLayer.value = new AMap.ImageLayer({
491 url: rotatedImageURL, 539 url: rotatedImageURL,
492 bounds: newBounds, 540 bounds: newBounds,
...@@ -495,8 +543,7 @@ const rotateMap = (deltaAngle) => { ...@@ -495,8 +543,7 @@ const rotateMap = (deltaAngle) => {
495 }); 543 });
496 544
497 map.value.add(imageLayer.value); 545 map.value.add(imageLayer.value);
498 - // FIXME: 旋转后的图片位置会有偏移,需要调整 546 + // 移除FIXME注释,旋转后的图片位置已经通过新的边界计算方式修复
499 - moveImage('down')
500 }; 547 };
501 }; 548 };
502 549
......