@@ -45,6 +45,14 @@ export interface GradientStop {
4545 color : string ;
4646}
4747
48+ export interface PrintCircularOptions {
49+ /**
50+ * The fit options, this is similar to CSS's object-fit.
51+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
52+ */
53+ fit ?: 'fill' | 'contain' | 'cover' | 'none' ;
54+ }
55+
4856export type GlobalCompositeOperation = CanvasRenderingContext2D [ 'globalCompositeOperation' ] ;
4957export type AntiAlias = CanvasRenderingContext2D [ 'antialias' ] ;
5058export type TextDrawingMode = CanvasRenderingContext2D [ 'textDrawingMode' ] ;
@@ -643,17 +651,17 @@ export class Canvas {
643651 * @param height The height to draw the image in the destination canvas. This allows scaling of the drawn image. If not specified, the image is not scaled in height when drawn.
644652 * @param radius The radius for the circle
645653 */
646- public printCircularImage ( imageOrBuffer : ImageResolvable , x : number , y : number , radius : number ) : this {
647- const ratio = imageOrBuffer . width / imageOrBuffer . height ;
648- const [ posX , posY , sizeX , sizeY ] =
649- ratio === 1
650- ? [ x - radius , y - radius , radius * 2 , radius * 2 ]
651- : ratio > 1
652- ? [ x - radius , y - radius / ratio , radius * 2 , ( radius * 2 ) / ratio ]
653- : [ x - radius * ratio , y - radius , radius * 2 * ratio , radius * 2 ] ;
654+ public printCircularImage (
655+ imageOrBuffer : ImageResolvable ,
656+ x : number ,
657+ y : number ,
658+ radius : number ,
659+ { fit = 'fill' } : PrintCircularOptions = { }
660+ ) : this {
661+ const { positionX , positionY , sizeX , sizeY } = Canvas . resolveCircularCoordinates ( imageOrBuffer , x , y , radius , fit ) ;
654662 return this . save ( )
655663 . createCircularClip ( x , y , radius , 0 , Math . PI * 2 , false )
656- . printImage ( imageOrBuffer , posX , posY , sizeX , sizeY )
664+ . printImage ( imageOrBuffer , positionX , positionY , sizeX , sizeY )
657665 . restore ( ) ;
658666 }
659667
@@ -1702,4 +1710,77 @@ export class Canvas {
17021710 }
17031711 return wrappedText ;
17041712 }
1713+
1714+ private static resolveCircularCoordinates (
1715+ imageOrBuffer : ImageResolvable ,
1716+ x : number ,
1717+ y : number ,
1718+ radius : number ,
1719+ fit : NonNullable < PrintCircularOptions [ 'fit' ] >
1720+ ) : ResolvedCircularCoordinates {
1721+ const { width, height } = imageOrBuffer ;
1722+ if ( fit === 'none' ) {
1723+ return {
1724+ positionX : x - width / 2 ,
1725+ positionY : y - height / 2 ,
1726+ sizeX : width ,
1727+ sizeY : height
1728+ } ;
1729+ }
1730+
1731+ const ratio = width / height ;
1732+ const diameter = radius * 2 ;
1733+
1734+ if ( fit === 'fill' || ratio === 1 ) {
1735+ return {
1736+ positionX : x - radius ,
1737+ positionY : y - radius ,
1738+ sizeX : diameter ,
1739+ sizeY : diameter
1740+ } ;
1741+ }
1742+
1743+ if ( fit === 'contain' ) {
1744+ return ratio > 1
1745+ ? {
1746+ positionX : x - radius ,
1747+ positionY : y - radius / ratio ,
1748+ sizeX : diameter ,
1749+ sizeY : diameter / ratio
1750+ }
1751+ : {
1752+ positionX : x - radius * ratio ,
1753+ positionY : y - radius ,
1754+ sizeX : diameter * ratio ,
1755+ sizeY : diameter
1756+ } ;
1757+ }
1758+
1759+ if ( ratio > 1 ) {
1760+ const sizeX = diameter * ratio ;
1761+ const sizeY = diameter ;
1762+ return {
1763+ positionX : x - sizeX / 2 ,
1764+ positionY : y - sizeY / 2 ,
1765+ sizeX,
1766+ sizeY
1767+ } ;
1768+ }
1769+
1770+ const sizeX = diameter ;
1771+ const sizeY = diameter / ratio ;
1772+ return {
1773+ positionX : x - sizeX / 2 ,
1774+ positionY : y - sizeY / 2 ,
1775+ sizeX,
1776+ sizeY
1777+ } ;
1778+ }
1779+ }
1780+
1781+ interface ResolvedCircularCoordinates {
1782+ positionX : number ;
1783+ positionY : number ;
1784+ sizeX : number ;
1785+ sizeY : number ;
17051786}
0 commit comments