Skip to content

Commit

Permalink
Merge pull request #30 from davidenke/feature/use-proxy
Browse files Browse the repository at this point in the history
This is a refactoring of how the polyfill is applied. This should now produce results closer to the native implementations. The filter results can now be seen side by side, which reveals some differences for e.g. `drop-shadow` and `saturation`.
  • Loading branch information
davidenke authored Aug 16, 2024
2 parents 370153e + 113e791 commit aee00a5
Show file tree
Hide file tree
Showing 62 changed files with 6,617 additions and 15,962 deletions.
14 changes: 0 additions & 14 deletions .eslintrc.cjs

This file was deleted.

12 changes: 7 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and release
name: test and build
on:
push:
branches: [main]
Expand All @@ -23,19 +23,21 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.13.0
node-version: 20.16.0
- uses: actions/cache@v3
with:
path: ~/.npm
key: v1-npm-deps-${{ hashFiles('**/package-lock.json') }}
restore-keys: v1-npm-deps-
- run: npm ci
- run: npx --package @rollup/rollup-linux-x64-gnu --package playwright playwright install --with-deps webkit
- run: npm install
- run: npm run lint
- run: npm run test:ci
- uses: mikepenz/action-junit-report@v4
if: success() || failure()
with:
check_name: "report"
include_passed: true
report_paths: "**/reports/junit.xml"

build:
Expand All @@ -45,7 +47,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.13.0
node-version: 20.16.0
- uses: actions/cache@v3
with:
path: ~/.npm
Expand Down Expand Up @@ -95,7 +97,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.13.0
node-version: 20.16.0
registry-url: https://registry.npmjs.org/
- uses: actions/download-artifact@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.13.0
20.16.0
4 changes: 1 addition & 3 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"singleQuote": true,
"jsxSingleQuote": false,
"trailingComma": "all",
"endOfLine": "auto",
"arrowParens": "avoid",
"tabWidth": 2,
"printWidth": 100
"tabWidth": 2
}
47 changes: 20 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
# context-filter-polyfill

[![Build Status](https://github.com/davidenke/context-filter-polyfill/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/davidenke/context-filter-polyfill)
[![NPM Version][npm-version-image]][npm-url]
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]

https://davidenke.github.io/context-filter-polyfill/
Examples: https://davidenke.github.io/context-filter-polyfill/

Polyfills [`CanvasRenderingContext2d.filter`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter) capability of adopting CSS3 filters to canvas contexts at least partially.

Successfully tested on

- macOS Safari
- iOS Safari
- Windows 10 IE11
- Windows 10 Edge 16-18
Right now ~~only WebKit [misses an implementation (Bugzilla #198416)](https://bugs.webkit.org/show_bug.cgi?id=198416)~~ all engines support it natively, despite Safari not having shipped it yet in the stable release channel.

## Installation

Expand All @@ -35,6 +32,15 @@ npm install context-filter-polyfill
import 'context-filter-polyfill';
```

## Changes in 0.4

Since version 0.4.0 the method of how the polyfill is applied has been reworked.
It now polyfills the filter on each drawing function call instead of applying it once on the context in the end.

This results in more accurate behavior compared to the native implementation.

The [polyfilled and native results](https://davidenke.github.io/context-filter-polyfill/) can be compared with a non-WebKit browser like Firefox or Chrome.

## Supported filters

- [`url`](<https://developer.mozilla.org/en-US/docs/Web/CSS/filter#url()>)
Expand All @@ -52,26 +58,13 @@ import 'context-filter-polyfill';

## See it in action

Just open the [integration demo](https://davidenke.github.io/context-filter-polyfill/) on Safari / iOS or IE11.

## Strategy

The polyfill is applied by the following steps:

1. monkey patching all properties of the `CanvasRenderingContext2d`
1. monkey patching all getters and setters of the `CanvasRenderingContext2d`
1. monkey patching all methods of the `CanvasRenderingContext2d`
Just open the [integration demo](https://davidenke.github.io/context-filter-polyfill/) on Safari / iOS.

These patches are proxying all changes to a **offscreen canvas** which applies the appropriate filter polyfills everytime a _drawing
function_ is called:
## License

- `clearRect`
- `drawImage`
- `fill`
- `fillRect`
- `fillText`
- `stroke`
- `strokeRect`
- `strokeText`
[MIT](LICENSE)

The contents of the **offscreen canvas** are applied back to the original canvas and is then resetted.
[npm-install-size-image]: https://badgen.net/packagephobia/install/context-filter-polyfill
[npm-install-size-url]: https://packagephobia.com/result?p=context-filter-polyfill
[npm-url]: https://npmjs.org/package/context-filter-polyfill
[npm-version-image]: https://badgen.net/npm/v/context-filter-polyfill
28 changes: 17 additions & 11 deletions esbuild.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { parseArgs } from 'node:util';
import { type BuildOptions, build, context } from 'esbuild';
import { dtsPlugin } from 'esbuild-plugin-d.ts';

import { build, type BuildOptions, context } from 'esbuild';
import copyStaticFiles from 'esbuild-copy-static-files';

const { values } = parseArgs({
options: {
Expand All @@ -11,26 +12,31 @@ const { values } = parseArgs({
const { port, serve } = values;

const options: BuildOptions = {
entryPoints: ['src/index.ts', 'src/index.html', 'src/mocks/mock-1.jpg', 'src/mocks/mock-2.png'],
entryPoints: [
'src/index.ts',
'src/polyfill.ts',

'src/example.ts',
'src/example.css',
'src/example.jpg',
],
outdir: 'dist',
format: 'esm',
bundle: true,
sourcemap: true,
minify: true,
splitting: false,
target: ['es6'],
loader: { '.html': 'copy', '.jpg': 'copy', '.png': 'copy' },
plugins: [dtsPlugin()],
loader: { '.jpg': 'copy' },
plugins: [
copyStaticFiles({ src: 'src/module.d.ts', dest: 'dist/index.d.ts' }),
copyStaticFiles({ src: 'src/example.html', dest: 'dist/index.html' }),
],
};

try {
if (serve) {
const ctx = await context({
...options,
banner: {
js: `new EventSource('/esbuild').addEventListener('change', () => location.reload())`,
},
});
const ctx = await context(options);
await ctx.watch();
await ctx.serve({ servedir: 'dist', port: Number(port) });
console.log(`> Serving on http://localhost:${port}`);
Expand Down
37 changes: 37 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @ts-check

import eslintJs from '@eslint/js';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintSimpleImportSort from 'eslint-plugin-simple-import-sort';
import eslintPluginUnusedImports from 'eslint-plugin-unused-imports';
import eslintTs from 'typescript-eslint';

export default eslintTs.config(
eslintJs.configs.recommended,
...eslintTs.configs.recommended,
eslintPluginPrettierRecommended,
{
ignores: [
'**/node_modules/',
'**/generated/',
'**/dist/',
'**/coverage/',
'**/reports/',
],
},
{
plugins: {
'simple-import-sort': eslintSimpleImportSort,
'unused-imports': eslintPluginUnusedImports,
},
rules: {
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'prettier/prettier': ['error'],
'unused-imports/no-unused-imports': 'error',
},
},
);
Loading

0 comments on commit aee00a5

Please sign in to comment.