diff --git a/.eslintrc b/.eslintrc
index 09c2e42..f872f23 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,3 +1,6 @@
{
- "extends": "@antfu"
+ "extends": "@antfu",
+ "rules": {
+ "vue/no-deprecated-html-element-is": "off"
+ }
}
diff --git a/README.md b/README.md
index 31c3aae..00cb588 100644
--- a/README.md
+++ b/README.md
@@ -14,12 +14,11 @@
-
###### Features
- 💚 Lightweight and flexible [Vue 3](https://vuejs.org/) library for creating [PixiJS](https://pixijs.com/) applications.
- ✏️ Provides a [Custom Vue Renderer](https://vuejs.org/api/custom-renderer.html#custom-renderer-api) that creates PixiJS objects instead of HTML elements.
-- 📦 Supports all PixiJS objects, such as `Container`, `Sprite`, `Graphics`, `Text`, etc
+- 📦 Supports all PixiJS objects, such as `Filter`, `Container`, `Sprite`, `Graphics`, `Text`, etc
- 🧑💻 Support specifying `texture` paths in templates to load texture objects
- ✨ All [events](https://pixijs.download/release/docs/PIXI.Sprite.html#onclick) emitted by PixiJS objects are supported
- 🗃️ Offers a [Assets](#assets) component for bundling assets.
@@ -154,6 +153,66 @@ function draw(g: Graphics) {
```
+## Filter
+
+To use `filter`, you need to place the Filter tag under the `` that sets the filter. Currently, the following filters are supported by default:
+
+- [BlurFilter](https://pixijs.download/release/docs/PIXI.BlurFilter.html)
+- [AlphaFilter](https://pixijs.download/release/docs/PIXI.AlphaFilter.html)
+- [DisplacementFilter](https://pixijs.download/release/docs/PIXI.DisplacementFilter.html)
+- [ColorMatrixFilter](https://pixijs.download/release/docs/PIXI.ColorMatrixFilter.html)
+- [NoiseFilter](https://pixijs.download/release/docs/PIXI.NoiseFilter.html)
+- [FXAAFilter](https://pixijs.download/release/docs/PIXI.FXAAFilter.html)
+
+Example of using `BlurFilter` with a Container:
+
+```html
+
+
+
+
+
+```
+
+### Custom Filter
+
+To use other filters, you can use the is attribute of ``. For example, to use the `ShockwaveFilter` in [pixi-filters](https://github.com/pixijs/filters):
+
+```html
+
+
+
+
+
+```
+
+## Namespaces
+
+To avoid conflicts with other tags, such as ``, you can use the `pixi-` prefix or capitalize the first letter with ``.
+
+```html
+
+```
+
+> Other components also support the `pixi-` prefix, so you can choose according to your personal preference.
+
## Assets
`vue3-pixi-renderer` provides a special component for bundling resources and obtaining resources from plugins.
@@ -236,7 +295,6 @@ onMounted(() => {
```
-
## Creating an pixi application manually
Using the custom renderer inside `vue3-pixi-renderer`
diff --git a/playground/src/components/CursorSprite.vue b/playground/src/components/CursorSprite.vue
index 549c3b7..52c9ad8 100644
--- a/playground/src/components/CursorSprite.vue
+++ b/playground/src/components/CursorSprite.vue
@@ -1,15 +1,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/src/elements/FXAAFilter.ts b/src/elements/FXAAFilter.ts
new file mode 100644
index 0000000..e5d05d6
--- /dev/null
+++ b/src/elements/FXAAFilter.ts
@@ -0,0 +1,31 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { FXAAFilter } from 'pixi.js'
+import type { AllowedFilterProps, ExtractFilterProps } from './props'
+
+interface Props extends ExtractFilterProps {}
+
+interface Events {}
+
+export type PixiFXAAFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/alphaFilter.ts b/src/elements/alphaFilter.ts
new file mode 100644
index 0000000..0493fd4
--- /dev/null
+++ b/src/elements/alphaFilter.ts
@@ -0,0 +1,33 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { AlphaFilter } from 'pixi.js'
+import type { AllowedFilterProps, ExtractFilterProps } from './props'
+
+interface Props extends ExtractFilterProps {
+
+}
+
+interface Events {}
+
+export type PixiAlphaFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/blurFilter.ts b/src/elements/blurFilter.ts
new file mode 100644
index 0000000..2956d1f
--- /dev/null
+++ b/src/elements/blurFilter.ts
@@ -0,0 +1,35 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { BlurFilter } from 'pixi.js'
+import type { AllowedFilterProps, ExtractFilterProps } from './props'
+
+interface Props extends ExtractFilterProps {
+ strength?: number
+ resolution?: number
+ kernelSize?: number
+}
+
+interface Events {}
+
+export type PixiBlurFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/colorMatrixFilter.ts b/src/elements/colorMatrixFilter.ts
new file mode 100644
index 0000000..e44b7a3
--- /dev/null
+++ b/src/elements/colorMatrixFilter.ts
@@ -0,0 +1,31 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { ColorMatrixFilter } from 'pixi.js'
+import type { AllowedFilterProps, ExtractFilterProps } from './props'
+
+interface Props extends ExtractFilterProps {}
+
+interface Events {}
+
+export type PixiColorMatrixFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/displacementFilter.ts b/src/elements/displacementFilter.ts
new file mode 100644
index 0000000..35bae4d
--- /dev/null
+++ b/src/elements/displacementFilter.ts
@@ -0,0 +1,33 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { DisplacementFilter, ISpriteMaskTarget } from 'pixi.js'
+import type { AllowedFilterProps, ExtractFilterProps } from './props'
+
+interface Props extends ExtractFilterProps {
+ sprite: ISpriteMaskTarget
+}
+
+interface Events {}
+
+export type PixiDisplacementFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/filter.ts b/src/elements/filter.ts
new file mode 100644
index 0000000..8e2f236
--- /dev/null
+++ b/src/elements/filter.ts
@@ -0,0 +1,34 @@
+import type {
+ ComponentCustomProps,
+ ComponentOptionsMixin,
+ DefineComponent,
+ VNodeProps,
+} from 'vue-demi'
+import type { AllowedFilterProps } from './props'
+
+interface Props {
+
+}
+
+interface Events {
+
+}
+
+export type PixiFilterComponent = DefineComponent<
+ Props,
+ {},
+ unknown,
+ {},
+ {},
+ ComponentOptionsMixin,
+ ComponentOptionsMixin,
+ (keyof Events)[],
+ keyof Events,
+ VNodeProps & AllowedFilterProps & ComponentCustomProps,
+ Readonly & {
+ [key in keyof Events as `on${Capitalize}`]?:
+ | ((...args: Events[key]) => any)
+ | undefined;
+ },
+ {}
+>
diff --git a/src/elements/props/index.ts b/src/elements/props/index.ts
index 80513a5..4dceb5c 100644
--- a/src/elements/props/index.ts
+++ b/src/elements/props/index.ts
@@ -63,3 +63,9 @@ export interface AllowedPixiProps {
zIndex?: number
}
+
+export interface AllowedFilterProps extends Partial> {
+ is?: (props: any) => Filter
+}
+
+export type ExtractFilterProps = Partial>
diff --git a/src/global.ts b/src/global.ts
index 24e1e9c..bd03dab 100644
--- a/src/global.ts
+++ b/src/global.ts
@@ -1,3 +1,4 @@
+import '@vue/runtime-core'
import type { PixiSimpleRopeComponent } from './elements/simpleRope'
import type { PixiSimplePlaneComponent } from './elements/simplePlane'
import type { PixiNineSlicePlaneComponent } from './elements/nineSlicePlane'
@@ -9,11 +10,33 @@ import type { PixiGraphicsComponent } from './elements/graphics'
import type { PixiContainerComponent } from './elements/container'
import type { PixiSpriteComponent } from './elements/sprite'
import type { PixiBitmapTextComponent } from './elements/bitmapText'
-
-import '@vue/runtime-core'
+import type { PixiFilterComponent } from './elements/filter'
+import type { PixiBlurFilterComponent } from './elements/blurFilter'
+import type { PixiAlphaFilterComponent } from './elements/alphaFilter'
+import type { PixiDisplacementFilterComponent } from './elements/displacementFilter'
+import type { PixiColorMatrixFilterComponent } from './elements/colorMatrixFilter'
+import type { PixiFXAAFilterComponent } from './elements/FXAAFilter'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
+ Filter: PixiFilterComponent
+ PixiFilter: PixiFilterComponent
+
+ BlurFilter: PixiBlurFilterComponent
+ PixiBlurFilter: PixiBlurFilterComponent
+
+ AlphaFilter: PixiAlphaFilterComponent
+ PixiAlphaFilter: PixiAlphaFilterComponent
+
+ DisplacementFilter: PixiDisplacementFilterComponent
+ PixiDisplacementFilter: PixiDisplacementFilterComponent
+
+ ColorMatrixFilter: PixiColorMatrixFilterComponent
+ PixiColorMatrixFilter: PixiColorMatrixFilterComponent
+
+ FXAAFilter: PixiFXAAFilterComponent
+ PixiFXAAFilter: PixiFXAAFilterComponent
+
Container: PixiContainerComponent
PixiContainer: PixiContainerComponent
diff --git a/src/renderer/elements.ts b/src/renderer/elements.ts
new file mode 100644
index 0000000..f3e6efa
--- /dev/null
+++ b/src/renderer/elements.ts
@@ -0,0 +1,86 @@
+import type { VNodeProps } from 'vue-demi'
+import {
+ AlphaFilter,
+ AnimatedSprite,
+ BitmapText,
+ BlurFilter,
+ ColorMatrixFilter,
+ Container,
+ DisplacementFilter,
+ FXAAFilter,
+ Graphics,
+ Mesh,
+ NineSlicePlane,
+ NoiseFilter,
+ SimpleMesh,
+ SimplePlane,
+ SimpleRope,
+ Sprite,
+ Text,
+ TilingSprite,
+} from 'pixi.js'
+import { normalizeTexture } from './utils'
+
+export type PixiCustomElement = (props: (VNodeProps & { [key: string]: any })) => any
+
+export const elements: Record = {
+ Container: () => new Container(),
+ Sprite: () => new Sprite(),
+ SimpleMesh: () => new SimpleMesh(),
+ Graphics: props => new Graphics(props?.geometry),
+ Text: props => new Text(
+ props.text,
+ props.style,
+ props.canvas,
+ ),
+ BitmapText: props => new BitmapText(
+ props.text,
+ props.style,
+ ),
+ TilingSprite: props => new TilingSprite(
+ normalizeTexture(props!.texture),
+ props.width,
+ props.height,
+ ),
+ AnimatedSprite: props => new AnimatedSprite(
+ props.textures,
+ props.autoUpdate,
+ ),
+ Mesh: props => new Mesh(
+ props.geometry,
+ props.shader,
+ props.state,
+ props.drawMode,
+ ),
+ NineSlicePlane: props => new NineSlicePlane(
+ normalizeTexture(props.texture),
+ ),
+ SimplePlane: props => new SimplePlane(
+ normalizeTexture(props.texture),
+ props.verticesX,
+ props.verticesY,
+ ),
+ SimpleRope: props => new SimpleRope(
+ normalizeTexture(props.texture),
+ props.points,
+ props.textureScale,
+ ),
+ BlurFilter: props => new BlurFilter(
+ props.strength,
+ props.quality,
+ props.resolution,
+ props.kernelSize,
+ ),
+ AlphaFilter: props => new AlphaFilter(props.alpha),
+ DisplacementFilter: props => new DisplacementFilter(
+ props.sprite,
+ props.scale,
+ ),
+ ColorMatrixFilter: () => new ColorMatrixFilter(),
+ NoiseFilter: props => new NoiseFilter(
+ props.noise,
+ props.seed,
+ ),
+ FXAAFilter: () => new FXAAFilter(),
+}
+
diff --git a/src/renderer/index.ts b/src/renderer/index.ts
index e05075a..4ae994f 100644
--- a/src/renderer/index.ts
+++ b/src/renderer/index.ts
@@ -1,61 +1,30 @@
import { camelize, createRenderer, warn } from 'vue-demi'
import {
- AnimatedSprite,
- BitmapText,
Container,
- Graphics,
- Mesh,
- NineSlicePlane,
- SimpleMesh,
- SimplePlane,
- SimpleRope,
- Sprite,
+ Filter,
Text,
- TilingSprite,
} from 'pixi.js'
-import { patchProp } from './props'
+import { patchProp } from './patch'
+import { elements } from './elements'
+import { isCustomFilter } from './utils'
interface CreatePixiRendererOptions {
prefix?: string
}
-const elements = {
- Container,
- Sprite,
- Graphics,
- Text,
- BitmapText,
- TilingSprite,
- AnimatedSprite,
- Mesh,
- NineSlicePlane,
- SimpleMesh,
- SimplePlane,
- SimpleRope,
-} as Record Container>
-
export function createPixiRenderer(options: CreatePixiRendererOptions = {}) {
const { prefix = 'pixi' } = options
return createRenderer({
createElement: (name, _, __, props) => {
- const Constructor = findConstructor(prefix, name)
+ const element = isCustomFilter(prefix, name)
+ ? props?.is?.(props)
+ : createPixiElement(prefix, name, props)
- if (!Constructor) {
- warn(`Unknown element ${name}`)
- return new Container()
- }
+ if (element instanceof Container)
+ element.filters = []
- switch (Constructor) {
- case Graphics:
- return new Constructor(props?.geometry)
- case Text:
- return new Constructor(props?.text, props?.style, props?.canvas)
- case BitmapText:
- return new Constructor(props?.text, props?.style)
- default:
- return new Constructor()
- }
+ return element
},
patchProp,
@@ -65,16 +34,16 @@ export function createPixiRenderer(options: CreatePixiRendererOptions = {}) {
createComment: () => new Container(),
remove: child => child.destroy(),
insert: (child, parent, anchor) => {
- if (anchor)
- parent.addChildAt(child, parent.getChildIndex(anchor))
+ if (child instanceof Filter)
+ insertFilter(child, parent, anchor)
else
- parent.addChild(child)
+ insertContainer(child, parent, anchor)
},
nextSibling: (node) => {
- const index = node.parent.getChildIndex(node)
- if (node.parent.children.length <= index + 1)
- return null
- return node.parent.getChildAt(index + 1) as Container ?? null
+ if (node instanceof Filter)
+ return nextSiblingFilter(node)
+ else
+ return nextSiblingContainer(node)
},
setElementText: (node, text) => {
node instanceof Text
@@ -87,18 +56,54 @@ export function createPixiRenderer(options: CreatePixiRendererOptions = {}) {
})
}
-function findConstructor(prefix: string, name: string) {
- let c
+function createPixiElement(prefix: string, name: string, props: any = {}) {
+ let is
if (name.startsWith(prefix)) {
name = camelize(name)
- c = elements[name.slice(prefix.length)]
+ is = elements[name.slice(prefix.length)]
}
else {
name = camelize(name)
name = name.charAt(0).toUpperCase() + name.slice(1)
- c = elements[name]
+ is = elements[name]
+ }
+ if (!is) {
+ warn(`Unknown element ${name}`)
+ return new Container()
+ }
+ return is(props)
+}
+
+function insertContainer(child: Container, parent: Container, anchor?: Container | null) {
+ if (anchor)
+ parent.addChildAt(child, parent.getChildIndex(anchor))
+ else
+ parent.addChild(child)
+}
+
+function insertFilter(child: any, parent: Container, _anchor: any) {
+ function remove() {
+ const index = parent.filters!.indexOf(child)
+ if (index !== -1)
+ parent.filters?.splice(index, 1)
}
- return c as undefined | (new (...args: any) => Container)
+ child.parent = parent
+ child.destroy = remove
+ parent.filters!.push(child)
+}
+
+function nextSiblingFilter(node: any) {
+ const index = node.parent.filters!.indexOf(node)
+ if (node.parent.filters!.length <= index + 1)
+ return null
+ return node
+}
+
+function nextSiblingContainer(node: Container) {
+ const index = node.parent.getChildIndex(node)
+ if (node.parent.children.length <= index + 1)
+ return null
+ return node.parent.getChildAt(index + 1) as Container ?? null
}
export const { createApp, render } = createPixiRenderer()
diff --git a/src/renderer/props.ts b/src/renderer/patch.ts
similarity index 97%
rename from src/renderer/props.ts
rename to src/renderer/patch.ts
index 6a87c56..6d68cdf 100644
--- a/src/renderer/props.ts
+++ b/src/renderer/patch.ts
@@ -8,10 +8,10 @@ import {
Mesh,
SimplePlane,
Sprite,
- Texture,
TilingSprite,
} from 'pixi.js'
import { setPointNumber, setPointObject, setValueProp } from './setter'
+import { normalizeTexture } from './utils'
const defaultBooleanProps = ['accessible', 'cullable', 'renderable', 'visible'] as const
const bitmapBooleanProps = ['dirty', 'roundPixels'] as const
@@ -194,9 +194,3 @@ export function patchBooleanProps(
}
return false
}
-
-export function normalizeTexture(value: Texture | string): Texture {
- if (typeof value === 'string')
- return Texture.from(value)
- return value
-}
diff --git a/src/renderer/utils.ts b/src/renderer/utils.ts
new file mode 100644
index 0000000..954dfec
--- /dev/null
+++ b/src/renderer/utils.ts
@@ -0,0 +1,17 @@
+import { Texture } from 'pixi.js'
+import { camelize } from 'vue-demi'
+
+export function normalizeTexture(value: Texture | string): Texture {
+ if (typeof value === 'string')
+ return Texture.from(value)
+ return value
+}
+
+export function isCustomFilter(prefix: string, name: string) {
+ const isPrefix = name.startsWith(prefix)
+ name = camelize(name)
+ name = name.charAt(0).toUpperCase() + name.slice(1)
+ return (
+ isPrefix && name.slice(prefix.length) === 'Filter'
+ ) || name === 'Filter'
+}