{"version":3,"file":"Gradient.min.mjs","sources":["../../../src/gradient/Gradient.ts"],"sourcesContent":["import { Color } from '../color/Color';\nimport { iMatrix } from '../constants';\nimport { parseTransformAttribute } from '../parser/parseTransformAttribute';\nimport type { FabricObject } from '../shapes/Object/FabricObject';\nimport type { TMat2D } from '../typedefs';\nimport { uid } from '../util/internals/uid';\nimport { pick } from '../util/misc/pick';\nimport { matrixToSVG } from '../util/misc/svgParsing';\nimport { linearDefaultCoords, radialDefaultCoords } from './constants';\nimport { parseColorStops } from './parser/parseColorStops';\nimport { parseCoords } from './parser/parseCoords';\nimport { parseType, parseGradientUnits } from './parser/misc';\nimport type {\n  ColorStop,\n  GradientCoords,\n  GradientOptions,\n  GradientType,\n  GradientUnits,\n  SVGOptions,\n} from './typedefs';\nimport { classRegistry } from '../ClassRegistry';\nimport { isPath } from '../util/typeAssertions';\n\n/**\n * Gradient class\n * @class Gradient\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#gradients}\n */\nexport class Gradient<\n  S,\n  T extends GradientType = S extends GradientType ? S : 'linear'\n> {\n  /**\n   * Horizontal offset for aligning gradients coming from SVG when outside pathgroups\n   * @type Number\n   * @default 0\n   */\n  declare offsetX: number;\n\n  /**\n   * Vertical offset for aligning gradients coming from SVG when outside pathgroups\n   * @type Number\n   * @default 0\n   */\n  declare offsetY: number;\n\n  /**\n   * A transform matrix to apply to the gradient before painting.\n   * Imported from svg gradients, is not applied with the current transform in the center.\n   * Before this transform is applied, the origin point is at the top left corner of the object\n   * plus the addition of offsetY and offsetX.\n   * @type Number[]\n   * @default null\n   */\n  declare gradientTransform?: TMat2D;\n\n  /**\n   * coordinates units for coords.\n   * If `pixels`, the number of coords are in the same unit of width / height.\n   * If set as `percentage` the coords are still a number, but 1 means 100% of width\n   * for the X and 100% of the height for the y. It can be bigger than 1 and negative.\n   * allowed values pixels or percentage.\n   * @type GradientUnits\n   * @default 'pixels'\n   */\n  declare gradientUnits: GradientUnits;\n\n  /**\n   * Gradient type linear or radial\n   * @type GradientType\n   * @default 'linear'\n   */\n  declare type: T;\n\n  /**\n   * Defines how the gradient is located in space and spread\n   * @type GradientCoords\n   */\n  declare coords: GradientCoords<T>;\n\n  /**\n   * Defines how many colors a gradient has and how they are located on the axis\n   * defined by coords\n   * @type GradientCoords\n   */\n  declare colorStops: ColorStop[];\n\n  /**\n   * If true, this object will not be exported during the serialization of a canvas\n   * @type boolean\n   */\n  declare excludeFromExport?: boolean;\n\n  /**\n   * ID used for SVG export functionalities\n   * @type number | string\n   */\n  declare readonly id: string | number;\n\n  static type = 'Gradient';\n\n  constructor({\n    type = 'linear' as T,\n    gradientUnits = 'pixels',\n    coords = {},\n    colorStops = [],\n    offsetX = 0,\n    offsetY = 0,\n    gradientTransform,\n    id,\n  }: GradientOptions<T>) {\n    this.id = id ? `${id}_${uid()}` : uid();\n    this.type = type;\n    this.gradientUnits = gradientUnits;\n    this.gradientTransform = gradientTransform;\n    this.offsetX = offsetX;\n    this.offsetY = offsetY;\n    this.coords = {\n      ...(this.type === 'radial' ? radialDefaultCoords : linearDefaultCoords),\n      ...coords,\n    } as GradientCoords<T>;\n    this.colorStops = colorStops.slice();\n  }\n\n  // isType<S extends GradientType>(type: S): this is Gradient<S> {\n  //   return (this.type as GradientType) === type;\n  // }\n\n  /**\n   * Adds another colorStop\n   * @param {Record<string, string>} colorStop Object with offset and color\n   * @return {Gradient} thisArg\n   */\n  addColorStop(colorStops: Record<string, string>) {\n    for (const position in colorStops) {\n      const color = new Color(colorStops[position]);\n      this.colorStops.push({\n        offset: parseFloat(position),\n        color: color.toRgb(),\n        opacity: color.getAlpha(),\n      });\n    }\n    return this;\n  }\n\n  /**\n   * Returns object representation of a gradient\n   * @param {string[]} [propertiesToInclude] Any properties that you might want to additionally include in the output\n   * @return {object}\n   */\n  toObject(propertiesToInclude?: (keyof this | string)[]) {\n    return {\n      ...pick(this, propertiesToInclude as (keyof this)[]),\n      type: this.type,\n      coords: this.coords,\n      colorStops: this.colorStops,\n      offsetX: this.offsetX,\n      offsetY: this.offsetY,\n      gradientUnits: this.gradientUnits,\n      gradientTransform: this.gradientTransform\n        ? [...this.gradientTransform]\n        : undefined,\n    };\n  }\n\n  /* _TO_SVG_START_ */\n  /**\n   * Returns SVG representation of an gradient\n   * @param {FabricObject} object Object to create a gradient for\n   * @return {String} SVG representation of an gradient (linear/radial)\n   */\n  toSVG(\n    object: FabricObject,\n    { additionalTransform: preTransform }: { additionalTransform?: string } = {}\n  ) {\n    const markup = [],\n      transform = (\n        this.gradientTransform\n          ? this.gradientTransform.concat()\n          : iMatrix.concat()\n      ) as TMat2D,\n      gradientUnits =\n        this.gradientUnits === 'pixels'\n          ? 'userSpaceOnUse'\n          : 'objectBoundingBox';\n    // colorStops must be sorted ascending, and guarded against deep mutations\n    const colorStops = this.colorStops\n      .map((colorStop) => ({ ...colorStop }))\n      .sort((a, b) => {\n        return a.offset - b.offset;\n      });\n\n    let offsetX = -this.offsetX,\n      offsetY = -this.offsetY;\n    if (gradientUnits === 'objectBoundingBox') {\n      offsetX /= object.width;\n      offsetY /= object.height;\n    } else {\n      offsetX += object.width / 2;\n      offsetY += object.height / 2;\n    }\n    // todo what about polygon/polyline?\n    if (isPath(object) && this.gradientUnits !== 'percentage') {\n      offsetX -= object.pathOffset.x;\n      offsetY -= object.pathOffset.y;\n    }\n    transform[4] -= offsetX;\n    transform[5] -= offsetY;\n\n    const commonAttributes = [\n      `id=\"SVGID_${this.id}\"`,\n      `gradientUnits=\"${gradientUnits}\"`,\n      `gradientTransform=\"${\n        preTransform ? preTransform + ' ' : ''\n      }${matrixToSVG(transform)}\"`,\n      '',\n    ].join(' ');\n\n    if (this.type === 'linear') {\n      const { x1, y1, x2, y2 } = this.coords;\n      markup.push(\n        '<linearGradient ',\n        commonAttributes,\n        ' x1=\"',\n        x1,\n        '\" y1=\"',\n        y1,\n        '\" x2=\"',\n        x2,\n        '\" y2=\"',\n        y2,\n        '\">\\n'\n      );\n    } else if (this.type === 'radial') {\n      const { x1, y1, x2, y2, r1, r2 } = this\n        .coords as GradientCoords<'radial'>;\n      const needsSwap = r1 > r2;\n      // svg radial gradient has just 1 radius. the biggest.\n      markup.push(\n        '<radialGradient ',\n        commonAttributes,\n        ' cx=\"',\n        needsSwap ? x1 : x2,\n        '\" cy=\"',\n        needsSwap ? y1 : y2,\n        '\" r=\"',\n        needsSwap ? r1 : r2,\n        '\" fx=\"',\n        needsSwap ? x2 : x1,\n        '\" fy=\"',\n        needsSwap ? y2 : y1,\n        '\">\\n'\n      );\n      if (needsSwap) {\n        // svg goes from internal to external radius. if radius are inverted, swap color stops.\n        colorStops.reverse(); //  mutates array\n        colorStops.forEach((colorStop) => {\n          colorStop.offset = 1 - colorStop.offset;\n        });\n      }\n      const minRadius = Math.min(r1, r2);\n      if (minRadius > 0) {\n        // i have to shift all colorStops and add new one in 0.\n        const maxRadius = Math.max(r1, r2),\n          percentageShift = minRadius / maxRadius;\n        colorStops.forEach((colorStop) => {\n          colorStop.offset += percentageShift * (1 - colorStop.offset);\n        });\n      }\n    }\n\n    colorStops.forEach(({ color, offset, opacity }) => {\n      markup.push(\n        '<stop ',\n        'offset=\"',\n        offset * 100 + '%',\n        '\" style=\"stop-color:',\n        color,\n        typeof opacity !== 'undefined' ? ';stop-opacity: ' + opacity : ';',\n        '\"/>\\n'\n      );\n    });\n\n    markup.push(\n      this.type === 'linear' ? '</linearGradient>' : '</radialGradient>',\n      '\\n'\n    );\n\n    return markup.join('');\n  }\n  /* _TO_SVG_END_ */\n\n  /**\n   * Returns an instance of CanvasGradient\n   * @param {CanvasRenderingContext2D} ctx Context to render on\n   * @return {CanvasGradient}\n   */\n  toLive(ctx: CanvasRenderingContext2D): CanvasGradient {\n    const coords = this.coords as GradientCoords<'radial'>;\n    const gradient =\n      this.type === 'linear'\n        ? ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2)\n        : ctx.createRadialGradient(\n            coords.x1,\n            coords.y1,\n            coords.r1,\n            coords.x2,\n            coords.y2,\n            coords.r2\n          );\n\n    this.colorStops.forEach(({ color, opacity, offset }) => {\n      gradient.addColorStop(\n        offset,\n        typeof opacity !== 'undefined'\n          ? new Color(color).setAlpha(opacity).toRgba()\n          : color\n      );\n    });\n\n    return gradient;\n  }\n\n  static async fromObject(\n    options: GradientOptions<'linear'>\n  ): Promise<Gradient<'radial'>>;\n  static async fromObject(\n    options: GradientOptions<'radial'>\n  ): Promise<Gradient<'radial'>>;\n  static async fromObject(\n    options: GradientOptions<'linear'> | GradientOptions<'radial'>\n  ) {\n    return new this(options);\n  }\n\n  /* _FROM_SVG_START_ */\n  /**\n   * Returns {@link Gradient} instance from an SVG element\n   * @static\n   * @memberOf Gradient\n   * @param {SVGGradientElement} el SVG gradient element\n   * @param {FabricObject} instance\n   * @param {String} opacity A fill-opacity or stroke-opacity attribute to multiply to each stop's opacity.\n   * @param {SVGOptions} svgOptions an object containing the size of the SVG in order to parse correctly gradients\n   * that uses gradientUnits as 'userSpaceOnUse' and percentages.\n   * @return {Gradient} Gradient instance\n   * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement\n   * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement\n   *\n   *  @example\n   *\n   *  <linearGradient id=\"linearGrad1\">\n   *    <stop offset=\"0%\" stop-color=\"white\"/>\n   *    <stop offset=\"100%\" stop-color=\"black\"/>\n   *  </linearGradient>\n   *\n   *  OR\n   *\n   *  <linearGradient id=\"linearGrad2\">\n   *    <stop offset=\"0\" style=\"stop-color:rgb(255,255,255)\"/>\n   *    <stop offset=\"1\" style=\"stop-color:rgb(0,0,0)\"/>\n   *  </linearGradient>\n   *\n   *  OR\n   *\n   *  <radialGradient id=\"radialGrad1\">\n   *    <stop offset=\"0%\" stop-color=\"white\" stop-opacity=\"1\" />\n   *    <stop offset=\"50%\" stop-color=\"black\" stop-opacity=\"0.5\" />\n   *    <stop offset=\"100%\" stop-color=\"white\" stop-opacity=\"1\" />\n   *  </radialGradient>\n   *\n   *  OR\n   *\n   *  <radialGradient id=\"radialGrad2\">\n   *    <stop offset=\"0\" stop-color=\"rgb(255,255,255)\" />\n   *    <stop offset=\"0.5\" stop-color=\"rgb(0,0,0)\" />\n   *    <stop offset=\"1\" stop-color=\"rgb(255,255,255)\" />\n   *  </radialGradient>\n   *\n   */\n  static fromElement(\n    el: SVGGradientElement,\n    instance: FabricObject,\n    svgOptions: SVGOptions\n  ): Gradient<GradientType> {\n    const gradientUnits = parseGradientUnits(el);\n    const center = instance._findCenterFromElement();\n    return new this({\n      id: el.getAttribute('id') || undefined,\n      type: parseType(el),\n      coords: parseCoords(el, {\n        width: svgOptions.viewBoxWidth || svgOptions.width,\n        height: svgOptions.viewBoxHeight || svgOptions.height,\n      }),\n      colorStops: parseColorStops(el, svgOptions.opacity),\n      gradientUnits,\n      gradientTransform: parseTransformAttribute(\n        el.getAttribute('gradientTransform') || ''\n      ),\n      ...(gradientUnits === 'pixels'\n        ? {\n            offsetX: instance.width / 2 - center.x,\n            offsetY: instance.height / 2 - center.y,\n          }\n        : {\n            offsetX: 0,\n            offsetY: 0,\n          }),\n    });\n  }\n  /* _FROM_SVG_END_ */\n}\n\nclassRegistry.setClass(Gradient, 'gradient');\nclassRegistry.setClass(Gradient, 'linear');\nclassRegistry.setClass(Gradient, 'radial');\n"],"names":["Gradient","constructor","_ref","type","gradientUnits","coords","colorStops","offsetX","offsetY","gradientTransform","id","this","concat","uid","_objectSpread","radialDefaultCoords","linearDefaultCoords","slice","addColorStop","position","color","Color","push","offset","parseFloat","toRgb","opacity","getAlpha","toObject","propertiesToInclude","pick","undefined","toSVG","object","additionalTransform","preTransform","arguments","length","markup","transform","iMatrix","map","colorStop","sort","a","b","width","height","isPath","pathOffset","x","y","commonAttributes","matrixToSVG","join","x1","y1","x2","y2","r1","r2","needsSwap","reverse","forEach","minRadius","Math","min","percentageShift","max","_ref2","toLive","ctx","gradient","createLinearGradient","createRadialGradient","_ref3","setAlpha","toRgba","fromObject","options","fromElement","el","instance","svgOptions","parseGradientUnits","center","_findCenterFromElement","getAttribute","parseType","parseCoords","viewBoxWidth","viewBoxHeight","parseColorStops","parseTransformAttribute","_defineProperty","classRegistry","setClass"],"mappings":"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"}