Showing
1 changed file
with
71 additions
and
24 deletions
| 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 | ... | ... |
-
Please register or login to post a comment