object-sizing.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. /**
  2. * 用于计算图片的宽高比例
  3. * @see https://drafts.csswg.org/css-images-3/#sizing-terms
  4. *
  5. * ## 名词解释
  6. * ### intrinsic dimensions
  7. * 图片本身的尺寸
  8. *
  9. * ### specified size
  10. * 用户指定的元素尺寸
  11. *
  12. * ### concrete object size
  13. * 应用了 `objectFit` 之后图片的显示尺寸
  14. *
  15. * ### default object size
  16. */
  17. export function concreteRect(style, intrinsicSize, specifiedSize) {
  18. const isContain = style.objectFit === 'contain';
  19. const specifiedPosition = style.specifiedPosition || [0, 0];
  20. // ratio 越大表示矩形越"扁"
  21. const intrinsicRatio = intrinsicSize.width / intrinsicSize.height;
  22. const specifiedRatio = specifiedSize.width / specifiedSize.height;
  23. /** 图片原始尺寸与最终尺寸之比 */
  24. let concreteScale = 1;
  25. if ((intrinsicRatio > specifiedRatio && style.objectFit == 'contain') ||
  26. (intrinsicRatio <= specifiedRatio && style.objectFit == 'cover'))
  27. // 图片较"胖"时完整显示图片,图片较"瘦"时完全覆盖容器
  28. // 这两种情况下有 concreteRect.width = specifiedSize.width
  29. // 因为 concreteRect.width = intrinsicSize.width * concreteScale
  30. // 所以:
  31. concreteScale = specifiedSize.width / intrinsicSize.width;
  32. else if ((intrinsicRatio > specifiedRatio && style.objectFit == 'cover') ||
  33. (intrinsicRatio <= specifiedRatio && style.objectFit == 'contain'))
  34. // 图片较"瘦"时完整显示图片,图片较"胖"时完全覆盖容器
  35. // 这两种情况下有 concreteRect.height = specifiedSize.height
  36. // 因为 concreteRect.height = intrinsicSize.height * concreteScale
  37. // 所以:
  38. concreteScale = specifiedSize.height / intrinsicSize.height;
  39. else
  40. throw new Error('Unkonwn concreteScale');
  41. const concreteRectWidth = intrinsicSize.width * concreteScale;
  42. const concreteRectHeight = intrinsicSize.height * concreteScale;
  43. // 这里可以把 left top 的计算想象成投影
  44. const xRelativeOrigin = { left: 0, center: 0.5, right: 1 }[style.intrinsicPosition?.[0] || 'center'];
  45. const yRelativeOrigin = { top: 0, center: 0.5, bottom: 1 }[style.intrinsicPosition?.[1] || 'center'];
  46. let concreteRectLeft = (specifiedSize.width - concreteRectWidth) * xRelativeOrigin;
  47. let concreteRectTop = (specifiedSize.height - concreteRectHeight) * yRelativeOrigin;
  48. if (isContain) {
  49. concreteRectLeft += specifiedPosition[0];
  50. concreteRectTop += specifiedPosition[1];
  51. }
  52. // 这里有两个坐标系,一个是 specified (dist) 的坐标系,一个是 intrinsic (src) 的坐标系
  53. // 这里将两个坐标系的点位置进行变换
  54. // 例: 带入 x=0, y=0, 得到的结果就是 specifiedRect 的左上角在 intrinsic 坐标系下的坐标位置
  55. // 在 specified 坐标系下, intrinsic 的零点在 (concreteRectLeft, concreteRectTop), 缩放为 concreteScale
  56. // 所以有 x_dist = x_src * concreteScale + concreteRectLeft
  57. // y_dist = y_src * concreteScale + concreteRectTop
  58. const dist2src = (distX, distY) => [
  59. /* srcX = */ (distX - concreteRectLeft) / concreteScale,
  60. /* srcY = */ (distY - concreteRectTop) / concreteScale
  61. ];
  62. const [srcLeft, srcTop] = dist2src(0, 0);
  63. // srcRight = 图片 specified 框右边在 src 坐标系下的 x 坐标
  64. // srcBottom = 图片 specified 框下边在 src 坐标系下的 y 坐标
  65. const [srcRight, srcBottom] = dist2src(specifiedSize.width, specifiedSize.height);
  66. // 这里要对 src 和 disc 两个框进行约束
  67. return {
  68. sx: Math.max(srcLeft, 0),
  69. sy: Math.max(srcTop, 0),
  70. sw: Math.min(srcRight - srcLeft, intrinsicSize.width),
  71. sh: Math.min(srcBottom - srcTop, intrinsicSize.height),
  72. dx: isContain ? Math.max(concreteRectLeft, 0) : specifiedPosition[0],
  73. dy: isContain ? Math.max(concreteRectTop, 0) : specifiedPosition[1],
  74. dw: Math.min(concreteRectWidth, specifiedSize.width),
  75. dh: Math.min(concreteRectHeight, specifiedSize.height)
  76. };
  77. }
  78. //# sourceMappingURL=object-sizing.js.map