Skip to content

Commit

Permalink
@astrojs/image: add a background option/prop to replace the alpha l…
Browse files Browse the repository at this point in the history
…ayer (#4642)

* Added `background` option and prop.
This optional color specifies which background to use when removing the
alpha channel if the output format doesn't support transparency.

* Modified existing tests

* Fixed wrong dimensions in tests

* Fixing a few instances of jpeg vs jpg

* Added color checking

* working on the tests

* tests are now passing

* Adding tests

* Added tests for background color

* no need to test with subpath

* Added fixture

* Renamed test fixture for background-color

* skipping test until fixed

* Typo

* Working on tests

* tests are passing

* Updated readme and added changeset

* Updated lockfile

* Updated lockfile

* Updated lockfile

Co-authored-by: Tony Sullivan <tony.f.sullivan@outlook.com>
  • Loading branch information
beeb and Tony Sullivan committed Sep 7, 2022
1 parent 93c3aee commit e4348a4
Show file tree
Hide file tree
Showing 28 changed files with 1,602 additions and 991 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-baboons-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/image': minor
---

Added a `background` option to specify a background color to replace transparent pixels (alpha layer).
54 changes: 45 additions & 9 deletions packages/integrations/image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ This integration provides `<Image />` and `<Picture>` components as well as a ba


### Quick Install

The `astro add` command-line tool automates the installation for you. Run one of the following commands in a new terminal window. (If you aren't sure which package manager you're using, run the first command.) Then, follow the prompts, and type "y" in the terminal (meaning "yes") for each one.

```sh
# Using NPM
npx astro add image
Expand All @@ -35,13 +35,13 @@ yarn astro add image
# Using PNPM
pnpm astro add image
```

Finally, in the terminal window running Astro, press `CTRL+C` and then restart the dev server.

If you run into any issues, [feel free to report them to us on GitHub](https://github.com/withastro/astro/issues) and try the manual installation steps below.

### Manual Install

First, install the `@astrojs/image` package using your package manager. If you're using npm or aren't sure, run this in the terminal:
```sh
npm install @astrojs/image
Expand All @@ -57,7 +57,7 @@ export default {
// ...
integrations: [image()],
}
```
```
Then, restart the dev server.

### Update `env.d.ts`
Expand Down Expand Up @@ -190,6 +190,24 @@ A `string` can be provided in the form of `{width}:{height}`, ex: `16:9` or `3:4

A `number` can also be provided, useful when the aspect ratio is calculated at build time. This can be an inline number such as `1.777` or inlined as a JSX expression like `aspectRatio={16/9}`.

#### background

<p>

**Type:** `ColorDefinition`<br>
**Default:** `undefined`
</p>

The background color to use for replacing the alpha channel with `sharp`'s `flatten` method. In case the output format
doesn't support transparency (i.e. `jpeg`), it's advisable to include a background color, otherwise black will be used
as default replacement for transparent pixels.

The parameter accepts a `string` as value.

The parameter can be a [named HTML color](https://www.w3schools.com/tags/ref_colornames.asp), a hexadecimal
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, or an RGB definition in the form
`rgb(100,100,100)`.

### `<Picture /`>

#### src
Expand Down Expand Up @@ -271,6 +289,24 @@ A `number` can also be provided, useful when the aspect ratio is calculated at b

The output formats to be used in the optimized image. If not provided, `webp` and `avif` will be used in addition to the original image format.

#### background

<p>

**Type:** `ColorDefinition`<br>
**Default:** `undefined`
</p>

The background color to use for replacing the alpha channel with `sharp`'s `flatten` method. In case the output format
doesn't support transparency (i.e. `jpeg`), it's advisable to include a background color, otherwise black will be used
as default replacement for transparent pixels.

The parameter accepts a `string` as value.

The parameter can be a [named HTML color](https://www.w3schools.com/tags/ref_colornames.asp), a hexadecimal
color representation with 3 or 6 hexadecimal characters in the form `#123[abc]`, or an RGB definition in the form
`rgb(100,100,100)`.

### `getImage`

This is the helper function used by the `<Image />` component to build `<img />` attributes for the transformed image. This helper can be used directly for more complex use cases that aren't currently supported by the `<Image />` component.
Expand Down Expand Up @@ -307,7 +343,7 @@ The integration can be configured to run with a different image service, either

### config.serviceEntryPoint

The `serviceEntryPoint` should resolve to the image service installed from NPM. The default entry point is `@astrojs/image/sharp`, which resolves to the entry point exported from this integration's `package.json`.

```js
Expand Down Expand Up @@ -342,7 +378,7 @@ export default {
## Examples

### Local images

Image files in your project's `src` directory can be imported in frontmatter and passed directly to the `<Image />` component. All other properties are optional and will default to the original image file's properties if not provided.

```astro
Expand Down Expand Up @@ -371,7 +407,7 @@ import heroImage from '../assets/hero.png';

Files in the `/public` directory are always served or copied as-is, with no processing. We recommend that local images are always kept in `src/` so that Astro can transform, optimize and bundle them. But if you absolutely must keep an image in `public/`, use its relative URL path as the image's `src=` attribute. It will be treated as a remote image, which requires an `aspectRatio` attribute.

Alternatively, you can import an image from your `public/` directory in your frontmatter and use a variable in your `src=` attribute. You cannot, however, import this directly inside the component as its `src` value.
Alternatively, you can import an image from your `public/` directory in your frontmatter and use a variable in your `src=` attribute. You cannot, however, import this directly inside the component as its `src` value.

For example, use an image located at `public/social.png` in either static or SSR builds like so:

Expand All @@ -386,7 +422,7 @@ import socialImage from '/social.png';
```

### Remote images

Remote images can be transformed with the `<Image />` component. The `<Image />` component needs to know the final dimensions for the `<img />` element to avoid content layout shifts. For remote images, this means you must either provide `width` and `height`, or one of the dimensions plus the required `aspectRatio`.

```astro
Expand Down
4 changes: 3 additions & 1 deletion packages/integrations/image/components/Picture.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface RemoteImageProps
widths: number[];
aspectRatio: TransformOptions['aspectRatio'];
formats?: OutputFormat[];
background: TransformOptions['background'];
}
export type Props = LocalImageProps | RemoteImageProps;
Expand All @@ -37,6 +38,7 @@ const {
sizes,
widths,
aspectRatio,
background,
formats = ['avif', 'webp'],
loading = 'lazy',
decoding = 'async',
Expand All @@ -47,7 +49,7 @@ if (alt === undefined || alt === null) {
warnForMissingAlt();
}
const { image, sources } = await getPicture({ src, widths, formats, aspectRatio });
const { image, sources } = await getPicture({ src, widths, formats, aspectRatio, background });
---

<picture {...attrs}>
Expand Down
10 changes: 8 additions & 2 deletions packages/integrations/image/src/lib/get-image.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
/// <reference types="astro/astro-jsx" />
import type { ImageService, OutputFormat, TransformOptions } from '../loaders/index.js';
import type {
ColorDefinition,
ImageService,
OutputFormat,
TransformOptions,
} from '../loaders/index.js';
import { isSSRService, parseAspectRatio } from '../loaders/index.js';
import sharp from '../loaders/sharp.js';
import { isRemoteImage } from '../utils/paths.js';
Expand Down Expand Up @@ -63,7 +68,7 @@ async function resolveTransform(input: GetImageTransform): Promise<TransformOpti
// resolve the metadata promise, usually when the ESM import is inlined
const metadata = 'then' in input.src ? (await input.src).default : input.src;

let { width, height, aspectRatio, format = metadata.format, ...rest } = input;
let { width, height, aspectRatio, background, format = metadata.format, ...rest } = input;

if (!width && !height) {
// neither dimension was provided, use the file metadata
Expand All @@ -86,6 +91,7 @@ async function resolveTransform(input: GetImageTransform): Promise<TransformOpti
height,
aspectRatio,
format: format as OutputFormat,
background: background as ColorDefinition | undefined,
};
}

Expand Down
3 changes: 3 additions & 0 deletions packages/integrations/image/src/lib/get-picture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface GetPictureParams {
widths: number[];
formats: OutputFormat[];
aspectRatio?: TransformOptions['aspectRatio'];
background?: TransformOptions['background'];
}

export interface GetPictureResult {
Expand Down Expand Up @@ -64,6 +65,7 @@ export async function getPicture(params: GetPictureParams): Promise<GetPictureRe
format,
width,
height: Math.round(width / aspectRatio!),
background: params.background,
});
return `${img.src} ${width}w`;
})
Expand All @@ -83,6 +85,7 @@ export async function getPicture(params: GetPictureParams): Promise<GetPictureRe
width: Math.max(...widths),
aspectRatio,
format: allFormats[allFormats.length - 1],
background: params.background,
});

const sources = await Promise.all(allFormats.map((format) => getSource(format)));
Expand Down

0 comments on commit e4348a4

Please sign in to comment.