pixel-flow-vue 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +181 -0
- package/dist/index.d.ts +122 -0
- package/dist/pixel-flow-vue.js +129 -0
- package/dist/pixel-flow-vue.js.map +1 -0
- package/dist/pixel-flow-vue.umd.cjs +2 -0
- package/dist/pixel-flow-vue.umd.cjs.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +41 -24
- package/dist/pixel-flow-vue.css +0 -2
- package/dist/pixel-flow.es.js +0 -22
- package/dist/pixel-flow.umd.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pixel-flow-vue contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# pixel-flow-vue
|
|
2
|
+
|
|
3
|
+
> A lightweight **Vue 3** pixel-flow animation plugin with full **TypeScript** support.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/pixel-flow-vue)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://vuejs.org/)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
- 🎨 **Pixel-art style** flowing particle animation via Canvas
|
|
14
|
+
- ⚡ **Vite + Rollup** built — ESM & UMD bundles included
|
|
15
|
+
- 🔷 **Full TypeScript** types exported out of the box
|
|
16
|
+
- 🎛️ Highly configurable: colors, speed, particle count, pixel size
|
|
17
|
+
- 🪄 `start()` / `stop()` control via component `ref`
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📦 Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install pixel-flow-vue
|
|
25
|
+
# or
|
|
26
|
+
pnpm add pixel-flow-vue
|
|
27
|
+
# or
|
|
28
|
+
yarn add pixel-flow-vue
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🚀 Quick Start
|
|
34
|
+
|
|
35
|
+
### Global registration
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// main.ts
|
|
39
|
+
import { createApp } from 'vue'
|
|
40
|
+
import App from './App.vue'
|
|
41
|
+
import PixelFlowVue from 'pixel-flow-vue'
|
|
42
|
+
import 'pixel-flow-vue/style.css'
|
|
43
|
+
|
|
44
|
+
createApp(App).use(PixelFlowVue).mount('#app')
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```vue
|
|
48
|
+
<!-- anywhere in your app -->
|
|
49
|
+
<template>
|
|
50
|
+
<PixelFlow :width="800" :height="450" />
|
|
51
|
+
</template>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### On-demand import
|
|
55
|
+
|
|
56
|
+
```vue
|
|
57
|
+
<template>
|
|
58
|
+
<PixelFlow
|
|
59
|
+
ref="flowRef"
|
|
60
|
+
:width="800"
|
|
61
|
+
:height="450"
|
|
62
|
+
:colors="['#00f5ff', '#7b2fff']"
|
|
63
|
+
:particle-count="200"
|
|
64
|
+
:speed="1.5"
|
|
65
|
+
:pixel-size="5"
|
|
66
|
+
@ready="onReady"
|
|
67
|
+
/>
|
|
68
|
+
<button @click="flowRef?.stop()">Stop</button>
|
|
69
|
+
<button @click="flowRef?.start()">Start</button>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<script setup lang="ts">
|
|
73
|
+
import { ref } from 'vue'
|
|
74
|
+
import { PixelFlow } from 'pixel-flow-vue'
|
|
75
|
+
import type { PixelFlowInstance } from 'pixel-flow-vue'
|
|
76
|
+
|
|
77
|
+
const flowRef = ref<PixelFlowInstance | null>(null)
|
|
78
|
+
|
|
79
|
+
function onReady() {
|
|
80
|
+
console.log('Pixel flow is ready!')
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Use the core engine directly (headless)
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { createPixelFlow } from 'pixel-flow-vue'
|
|
89
|
+
|
|
90
|
+
const canvas = document.querySelector<HTMLCanvasElement>('#myCanvas')!
|
|
91
|
+
const stop = createPixelFlow({
|
|
92
|
+
canvas,
|
|
93
|
+
colors: ['#ff2fa0', '#ffdd00'],
|
|
94
|
+
particleCount: 80,
|
|
95
|
+
speed: 2,
|
|
96
|
+
pixelSize: 6,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Later: stop the animation
|
|
100
|
+
stop()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 🎛️ Props
|
|
106
|
+
|
|
107
|
+
| Prop | Type | Default | Description |
|
|
108
|
+
| --------------- | ---------- | --------------------------------------------------- | ------------------------------ |
|
|
109
|
+
| `width` | `number` | `600` | Canvas width (px) |
|
|
110
|
+
| `height` | `number` | `400` | Canvas height (px) |
|
|
111
|
+
| `colors` | `string[]` | `['#00f5ff','#7b2fff','#ff2fa0','#ffdd00']` | Particle color palette |
|
|
112
|
+
| `particleCount` | `number` | `120` | Number of particles |
|
|
113
|
+
| `speed` | `number` | `1` | Speed multiplier (0.1 ~ 5) |
|
|
114
|
+
| `pixelSize` | `number` | `4` | Base pixel block size (px) |
|
|
115
|
+
| `background` | `string` | `'#0d0d0d'` | Canvas background color |
|
|
116
|
+
| `autoPlay` | `boolean` | `true` | Start animation on mount |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 📡 Events
|
|
121
|
+
|
|
122
|
+
| Event | Payload | Description |
|
|
123
|
+
| ------- | -------------- | ------------------------------------ |
|
|
124
|
+
| `ready` | — | Fired when the animation loop starts |
|
|
125
|
+
| `tick` | `frame: number`| Fired on every animation frame |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 🔌 Exposed Methods (via `ref`)
|
|
130
|
+
|
|
131
|
+
| Method | Description |
|
|
132
|
+
| --------- | ------------------------ |
|
|
133
|
+
| `start()` | Start / restart the loop |
|
|
134
|
+
| `stop()` | Stop the animation loop |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📁 Package Exports
|
|
139
|
+
|
|
140
|
+
| Import path | Description |
|
|
141
|
+
| ------------------------- | --------------------- |
|
|
142
|
+
| `pixel-flow-vue` | Plugin + component |
|
|
143
|
+
| `pixel-flow-vue/style.css`| Component styles |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 🔧 Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Clone and install
|
|
151
|
+
git clone https://github.com/your-username/pixel-flow-vue.git
|
|
152
|
+
cd pixel-flow-vue
|
|
153
|
+
pnpm install
|
|
154
|
+
|
|
155
|
+
# Start dev server (playground)
|
|
156
|
+
pnpm dev
|
|
157
|
+
|
|
158
|
+
# Build for production
|
|
159
|
+
pnpm build
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 📤 Publishing to npm
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# 1. Log in to npm
|
|
168
|
+
npm login
|
|
169
|
+
|
|
170
|
+
# 2. Build
|
|
171
|
+
pnpm build
|
|
172
|
+
|
|
173
|
+
# 3. Publish
|
|
174
|
+
npm publish --access public
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 📄 License
|
|
180
|
+
|
|
181
|
+
[MIT](LICENSE) © your-username
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { App } from 'vue';
|
|
2
|
+
import { ComponentOptionsMixin } from 'vue';
|
|
3
|
+
import { ComponentProvideOptions } from 'vue';
|
|
4
|
+
import { DefineComponent } from 'vue';
|
|
5
|
+
import { ExtractPropTypes } from 'vue';
|
|
6
|
+
import { PropType } from 'vue';
|
|
7
|
+
import { PublicProps } from 'vue';
|
|
8
|
+
|
|
9
|
+
declare type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
10
|
+
|
|
11
|
+
declare type __VLS_Prettify<T> = {
|
|
12
|
+
[K in keyof T]: T[K];
|
|
13
|
+
} & {};
|
|
14
|
+
|
|
15
|
+
declare type __VLS_TypePropsToRuntimeProps<T> = {
|
|
16
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
17
|
+
type: PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
18
|
+
} : {
|
|
19
|
+
type: PropType<T[K]>;
|
|
20
|
+
required: true;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
declare type __VLS_WithDefaults<P, D> = {
|
|
25
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
|
|
26
|
+
default: D[K];
|
|
27
|
+
}> : P[K];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export declare function createPixelFlow(options: PixelFlowOptions): () => void;
|
|
31
|
+
|
|
32
|
+
export declare const PixelFlow: DefineComponent<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
colors: () => string[];
|
|
36
|
+
particleCount: number;
|
|
37
|
+
speed: number;
|
|
38
|
+
pixelSize: number;
|
|
39
|
+
background: string;
|
|
40
|
+
autoPlay: boolean;
|
|
41
|
+
}>>, {
|
|
42
|
+
start: typeof start;
|
|
43
|
+
stop: typeof stop_2;
|
|
44
|
+
}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
|
|
45
|
+
ready: () => void;
|
|
46
|
+
tick: (frame: number) => void;
|
|
47
|
+
}, string, PublicProps, Readonly<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<Props>, {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
colors: () => string[];
|
|
51
|
+
particleCount: number;
|
|
52
|
+
speed: number;
|
|
53
|
+
pixelSize: number;
|
|
54
|
+
background: string;
|
|
55
|
+
autoPlay: boolean;
|
|
56
|
+
}>>> & Readonly<{
|
|
57
|
+
onReady?: (() => any) | undefined;
|
|
58
|
+
onTick?: ((frame: number) => any) | undefined;
|
|
59
|
+
}>, {
|
|
60
|
+
colors: string[];
|
|
61
|
+
particleCount: number;
|
|
62
|
+
speed: number;
|
|
63
|
+
pixelSize: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
background: string;
|
|
67
|
+
autoPlay: boolean;
|
|
68
|
+
}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
|
|
69
|
+
|
|
70
|
+
/** PixelFlow 组件对外暴露的 ref 类型 */
|
|
71
|
+
export declare interface PixelFlowInstance {
|
|
72
|
+
/** 开始 / 重启动画 */
|
|
73
|
+
start: () => void;
|
|
74
|
+
/** 暂停动画 */
|
|
75
|
+
stop: () => void;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** createPixelFlow 的配置参数 */
|
|
79
|
+
export declare interface PixelFlowOptions {
|
|
80
|
+
/** 目标 Canvas 元素 */
|
|
81
|
+
canvas: HTMLCanvasElement;
|
|
82
|
+
/** 粒子颜色列表 */
|
|
83
|
+
colors?: string[];
|
|
84
|
+
/** 粒子数量 */
|
|
85
|
+
particleCount?: number;
|
|
86
|
+
/** 速度倍率 (0.1 ~ 5) */
|
|
87
|
+
speed?: number;
|
|
88
|
+
/** 像素块大小(px) */
|
|
89
|
+
pixelSize?: number;
|
|
90
|
+
/** 每帧回调 */
|
|
91
|
+
onTick?: () => void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
declare const PixelFlowVue: {
|
|
95
|
+
install(app: App): void;
|
|
96
|
+
};
|
|
97
|
+
export default PixelFlowVue;
|
|
98
|
+
|
|
99
|
+
declare interface Props {
|
|
100
|
+
/** 画布宽度,默认 600 */
|
|
101
|
+
width?: number;
|
|
102
|
+
/** 画布高度,默认 400 */
|
|
103
|
+
height?: number;
|
|
104
|
+
/** 像素粒子颜色列表 */
|
|
105
|
+
colors?: string[];
|
|
106
|
+
/** 粒子数量,默认 120 */
|
|
107
|
+
particleCount?: number;
|
|
108
|
+
/** 粒子速度倍率 (0.1 ~ 5),默认 1 */
|
|
109
|
+
speed?: number;
|
|
110
|
+
/** 像素大小,默认 4 */
|
|
111
|
+
pixelSize?: number;
|
|
112
|
+
/** 背景色,默认 '#0d0d0d' */
|
|
113
|
+
background?: string;
|
|
114
|
+
/** 是否自动播放,默认 true */
|
|
115
|
+
autoPlay?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare function start(): void;
|
|
119
|
+
|
|
120
|
+
declare function stop_2(): void;
|
|
121
|
+
|
|
122
|
+
export { }
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { defineComponent as b, ref as P, computed as _, onMounted as S, onUnmounted as z, watch as C, openBlock as D, createElementBlock as A, normalizeStyle as F } from "vue";
|
|
2
|
+
function R(c) {
|
|
3
|
+
const {
|
|
4
|
+
canvas: i,
|
|
5
|
+
colors: s = ["#00f5ff", "#7b2fff", "#ff2fa0", "#ffdd00"],
|
|
6
|
+
particleCount: a = 120,
|
|
7
|
+
speed: f = 1,
|
|
8
|
+
pixelSize: h = 4,
|
|
9
|
+
onTick: l
|
|
10
|
+
} = c, x = i.getContext("2d");
|
|
11
|
+
if (!x) throw new Error("[pixel-flow-vue] Failed to get 2D context");
|
|
12
|
+
const e = x;
|
|
13
|
+
let d = 0, u = !0;
|
|
14
|
+
function m() {
|
|
15
|
+
const t = i.width, o = i.height;
|
|
16
|
+
return {
|
|
17
|
+
x: Math.random() * t,
|
|
18
|
+
y: Math.random() * o,
|
|
19
|
+
vx: (Math.random() - 0.5) * f * 1.5,
|
|
20
|
+
vy: (Math.random() - 0.5) * f * 1.5,
|
|
21
|
+
color: s[Math.floor(Math.random() * s.length)],
|
|
22
|
+
size: h * (0.5 + Math.random() * 0.8),
|
|
23
|
+
alpha: Math.random(),
|
|
24
|
+
alphaDir: Math.random() > 0.5 ? 1 : -1
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const n = Array.from({ length: a }, m);
|
|
28
|
+
function v(t) {
|
|
29
|
+
const o = i.width, r = i.height;
|
|
30
|
+
t.x += t.vx * f, t.y += t.vy * f, (t.x < 0 || t.x > o) && (t.vx *= -1), (t.y < 0 || t.y > r) && (t.vy *= -1), t.alpha += 0.012 * t.alphaDir, t.alpha >= 1 && (t.alpha = 1, t.alphaDir = -1), t.alpha <= 0.1 && (t.alpha = 0.1, t.alphaDir = 1);
|
|
31
|
+
}
|
|
32
|
+
function M(t) {
|
|
33
|
+
e.globalAlpha = t.alpha, e.fillStyle = t.color, e.fillRect(
|
|
34
|
+
Math.round(t.x),
|
|
35
|
+
Math.round(t.y),
|
|
36
|
+
Math.round(t.size),
|
|
37
|
+
Math.round(t.size)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function k() {
|
|
41
|
+
for (let o = 0; o < n.length; o++)
|
|
42
|
+
for (let r = o + 1; r < n.length; r++) {
|
|
43
|
+
const y = n[o].x - n[r].x, g = n[o].y - n[r].y, w = Math.sqrt(y * y + g * g);
|
|
44
|
+
w < 80 && (e.globalAlpha = (1 - w / 80) * 0.25, e.strokeStyle = n[o].color, e.lineWidth = 0.8, e.beginPath(), e.moveTo(n[o].x, n[o].y), e.lineTo(n[r].x, n[r].y), e.stroke());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function p() {
|
|
48
|
+
if (!u) return;
|
|
49
|
+
const t = i.width, o = i.height;
|
|
50
|
+
e.globalAlpha = 0.18, e.fillStyle = "#000", e.fillRect(0, 0, t, o), e.globalAlpha = 1;
|
|
51
|
+
for (const r of n)
|
|
52
|
+
v(r), M(r);
|
|
53
|
+
k(), l == null || l(), d = requestAnimationFrame(p);
|
|
54
|
+
}
|
|
55
|
+
return p(), function() {
|
|
56
|
+
u = !1, cancelAnimationFrame(d);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const B = ["width", "height"], q = /* @__PURE__ */ b({
|
|
60
|
+
__name: "PixelFlow",
|
|
61
|
+
props: {
|
|
62
|
+
width: { default: 600 },
|
|
63
|
+
height: { default: 400 },
|
|
64
|
+
colors: { default: () => ["#00f5ff", "#7b2fff", "#ff2fa0", "#ffdd00"] },
|
|
65
|
+
particleCount: { default: 120 },
|
|
66
|
+
speed: { default: 1 },
|
|
67
|
+
pixelSize: { default: 4 },
|
|
68
|
+
background: { default: "#0d0d0d" },
|
|
69
|
+
autoPlay: { type: Boolean, default: !0 }
|
|
70
|
+
},
|
|
71
|
+
emits: ["ready", "tick"],
|
|
72
|
+
setup(c, { expose: i, emit: s }) {
|
|
73
|
+
const a = c, f = s, h = P(null);
|
|
74
|
+
let l = null, x = 0;
|
|
75
|
+
const e = _(() => ({
|
|
76
|
+
display: "block",
|
|
77
|
+
background: a.background
|
|
78
|
+
}));
|
|
79
|
+
function d() {
|
|
80
|
+
if (!h.value) return;
|
|
81
|
+
l == null || l();
|
|
82
|
+
const m = {
|
|
83
|
+
canvas: h.value,
|
|
84
|
+
colors: a.colors,
|
|
85
|
+
particleCount: a.particleCount,
|
|
86
|
+
speed: a.speed,
|
|
87
|
+
pixelSize: a.pixelSize,
|
|
88
|
+
onTick: () => f("tick", ++x)
|
|
89
|
+
};
|
|
90
|
+
l = R(m), f("ready");
|
|
91
|
+
}
|
|
92
|
+
function u() {
|
|
93
|
+
l == null || l(), l = null;
|
|
94
|
+
}
|
|
95
|
+
return S(() => {
|
|
96
|
+
a.autoPlay && d();
|
|
97
|
+
}), z(() => {
|
|
98
|
+
u();
|
|
99
|
+
}), C(
|
|
100
|
+
() => [a.particleCount, a.speed, a.pixelSize, a.colors],
|
|
101
|
+
() => {
|
|
102
|
+
l && d();
|
|
103
|
+
},
|
|
104
|
+
{ deep: !0 }
|
|
105
|
+
), i({ start: d, stop: u }), (m, n) => (D(), A("canvas", {
|
|
106
|
+
ref_key: "canvasRef",
|
|
107
|
+
ref: h,
|
|
108
|
+
width: c.width,
|
|
109
|
+
height: c.height,
|
|
110
|
+
style: F(e.value),
|
|
111
|
+
class: "pixel-flow-canvas"
|
|
112
|
+
}, null, 12, B));
|
|
113
|
+
}
|
|
114
|
+
}), E = (c, i) => {
|
|
115
|
+
const s = c.__vccOpts || c;
|
|
116
|
+
for (const [a, f] of i)
|
|
117
|
+
s[a] = f;
|
|
118
|
+
return s;
|
|
119
|
+
}, I = /* @__PURE__ */ E(q, [["__scopeId", "data-v-89863d06"]]), j = {
|
|
120
|
+
install(c) {
|
|
121
|
+
c.component("PixelFlow", I);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
export {
|
|
125
|
+
I as PixelFlow,
|
|
126
|
+
R as createPixelFlow,
|
|
127
|
+
j as default
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=pixel-flow-vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixel-flow-vue.js","sources":["../src/core/pixelFlow.ts","../src/components/PixelFlow.vue","../src/index.ts"],"sourcesContent":["import type { PixelFlowOptions } from '../types'\n\n// -------------------------------------------------------\n// 粒子结构\n// -------------------------------------------------------\ninterface Particle {\n x: number\n y: number\n vx: number\n vy: number\n color: string\n size: number\n alpha: number\n alphaDir: number\n}\n\n// -------------------------------------------------------\n// 工厂函数:createPixelFlow\n// 返回一个 stop 函数,调用后停止动画循环\n// -------------------------------------------------------\nexport function createPixelFlow(options: PixelFlowOptions): () => void {\n const {\n canvas,\n colors = ['#00f5ff', '#7b2fff', '#ff2fa0', '#ffdd00'],\n particleCount = 120,\n speed = 1,\n pixelSize = 4,\n onTick,\n } = options\n\n const rawCtx = canvas.getContext('2d')\n if (!rawCtx) throw new Error('[pixel-flow-vue] Failed to get 2D context')\n // After the null-check, rebind as non-nullable so TypeScript is satisfied\n // inside every nested closure (TS cannot narrow across function boundaries).\n const ctx: CanvasRenderingContext2D = rawCtx\n\n let rafId = 0\n let running = true\n\n // ---- 初始化粒子 ----\n function makeParticle(): Particle {\n const w = canvas.width\n const h = canvas.height\n return {\n x: Math.random() * w,\n y: Math.random() * h,\n vx: (Math.random() - 0.5) * speed * 1.5,\n vy: (Math.random() - 0.5) * speed * 1.5,\n color: colors[Math.floor(Math.random() * colors.length)],\n size: pixelSize * (0.5 + Math.random() * 0.8),\n alpha: Math.random(),\n alphaDir: Math.random() > 0.5 ? 1 : -1,\n }\n }\n\n const particles: Particle[] = Array.from({ length: particleCount }, makeParticle)\n\n // ---- 更新单个粒子 ----\n function updateParticle(p: Particle): void {\n const w = canvas.width\n const h = canvas.height\n\n p.x += p.vx * speed\n p.y += p.vy * speed\n\n // 边界回弹\n if (p.x < 0 || p.x > w) p.vx *= -1\n if (p.y < 0 || p.y > h) p.vy *= -1\n\n // 呼吸透明度\n p.alpha += 0.012 * p.alphaDir\n if (p.alpha >= 1) { p.alpha = 1; p.alphaDir = -1 }\n if (p.alpha <= 0.1) { p.alpha = 0.1; p.alphaDir = 1 }\n }\n\n // ---- 绘制单个像素块 ----\n function drawParticle(p: Particle): void {\n ctx.globalAlpha = p.alpha\n ctx.fillStyle = p.color\n ctx.fillRect(\n Math.round(p.x),\n Math.round(p.y),\n Math.round(p.size),\n Math.round(p.size),\n )\n }\n\n // ---- 连线:距离近的粒子之间画线 ----\n function drawConnections(): void {\n const maxDist = 80\n for (let i = 0; i < particles.length; i++) {\n for (let j = i + 1; j < particles.length; j++) {\n const dx = particles[i].x - particles[j].x\n const dy = particles[i].y - particles[j].y\n const dist = Math.sqrt(dx * dx + dy * dy)\n if (dist < maxDist) {\n ctx.globalAlpha = (1 - dist / maxDist) * 0.25\n ctx.strokeStyle = particles[i].color\n ctx.lineWidth = 0.8\n ctx.beginPath()\n ctx.moveTo(particles[i].x, particles[i].y)\n ctx.lineTo(particles[j].x, particles[j].y)\n ctx.stroke()\n }\n }\n }\n }\n\n // ---- 动画主循环 ----\n function loop(): void {\n if (!running) return\n\n const w = canvas.width\n const h = canvas.height\n\n // 拖尾效果:半透明清屏\n ctx.globalAlpha = 0.18\n ctx.fillStyle = '#000'\n ctx.fillRect(0, 0, w, h)\n\n ctx.globalAlpha = 1\n\n // 更新 & 绘制\n for (const p of particles) {\n updateParticle(p)\n drawParticle(p)\n }\n\n drawConnections()\n\n onTick?.()\n rafId = requestAnimationFrame(loop)\n }\n\n loop()\n\n // ---- 返回 stop 函数 ----\n return function stop() {\n running = false\n cancelAnimationFrame(rafId)\n }\n}\n","<template>\n <canvas\n ref=\"canvasRef\"\n :width=\"width\"\n :height=\"height\"\n :style=\"canvasStyle\"\n class=\"pixel-flow-canvas\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, computed, watch } from 'vue'\nimport type { CSSProperties } from 'vue'\nimport { createPixelFlow } from '../core/pixelFlow'\nimport type { PixelFlowOptions } from '../types'\n\n// ----------------------------------------------------------------\n// Props\n// ----------------------------------------------------------------\ninterface Props {\n /** 画布宽度,默认 600 */\n width?: number\n /** 画布高度,默认 400 */\n height?: number\n /** 像素粒子颜色列表 */\n colors?: string[]\n /** 粒子数量,默认 120 */\n particleCount?: number\n /** 粒子速度倍率 (0.1 ~ 5),默认 1 */\n speed?: number\n /** 像素大小,默认 4 */\n pixelSize?: number\n /** 背景色,默认 '#0d0d0d' */\n background?: string\n /** 是否自动播放,默认 true */\n autoPlay?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n width: 600,\n height: 400,\n colors: () => ['#00f5ff', '#7b2fff', '#ff2fa0', '#ffdd00'],\n particleCount: 120,\n speed: 1,\n pixelSize: 4,\n background: '#0d0d0d',\n autoPlay: true,\n})\n\n// ----------------------------------------------------------------\n// Emits\n// ----------------------------------------------------------------\nconst emit = defineEmits<{\n (e: 'ready'): void\n (e: 'tick', frame: number): void\n}>()\n\n// ----------------------------------------------------------------\n// Refs & state\n// ----------------------------------------------------------------\nconst canvasRef = ref<HTMLCanvasElement | null>(null)\nlet stopFn: (() => void) | null = null\nlet frame = 0\n\nconst canvasStyle = computed<CSSProperties>(() => ({\n display: 'block',\n background: props.background,\n}))\n\n// ----------------------------------------------------------------\n// Start / Stop helpers\n// ----------------------------------------------------------------\nfunction start() {\n if (!canvasRef.value) return\n stopFn?.()\n\n const options: PixelFlowOptions = {\n canvas: canvasRef.value,\n colors: props.colors,\n particleCount: props.particleCount,\n speed: props.speed,\n pixelSize: props.pixelSize,\n onTick: () => emit('tick', ++frame),\n }\n\n stopFn = createPixelFlow(options)\n emit('ready')\n}\n\nfunction stop() {\n stopFn?.()\n stopFn = null\n}\n\n// ----------------------------------------------------------------\n// Lifecycle\n// ----------------------------------------------------------------\nonMounted(() => {\n if (props.autoPlay) start()\n})\n\nonUnmounted(() => {\n stop()\n})\n\nwatch(\n () => [props.particleCount, props.speed, props.pixelSize, props.colors],\n () => {\n if (stopFn) start() // 重启以应用新配置\n },\n { deep: true }\n)\n\n// ----------------------------------------------------------------\n// Expose public API\n// ----------------------------------------------------------------\ndefineExpose({ start, stop })\n</script>\n\n<style scoped>\n.pixel-flow-canvas {\n border-radius: 4px;\n}\n</style>\n","import type { App } from 'vue'\nimport PixelFlow from './components/PixelFlow.vue'\nexport type { PixelFlowOptions, PixelFlowInstance } from './types'\nexport { createPixelFlow } from './core/pixelFlow'\n\n// ----------------------------------------------------------------\n// Vue 插件安装函数\n// ----------------------------------------------------------------\nconst PixelFlowVue = {\n install(app: App) {\n app.component('PixelFlow', PixelFlow)\n },\n}\n\n// ----------------------------------------------------------------\n// 导出\n// ----------------------------------------------------------------\nexport { PixelFlow }\nexport default PixelFlowVue\n"],"names":["createPixelFlow","options","canvas","colors","particleCount","speed","pixelSize","onTick","rawCtx","ctx","rafId","running","makeParticle","w","h","particles","updateParticle","p","drawParticle","drawConnections","i","j","dx","dy","dist","loop","props","__props","emit","__emit","canvasRef","ref","stopFn","frame","canvasStyle","computed","start","stop","onMounted","onUnmounted","watch","__expose","_createElementBlock","PixelFlowVue","app","PixelFlow"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(s,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(s=typeof globalThis<"u"?globalThis:s||self,n(s.PixelFlowVue={},s.Vue))})(this,function(s,n){"use strict";function w(f){const{canvas:c,colors:u=["#00f5ff","#7b2fff","#ff2fa0","#ffdd00"],particleCount:o=120,speed:d=1,pixelSize:x=4,onTick:l}=f,p=c.getContext("2d");if(!p)throw new Error("[pixel-flow-vue] Failed to get 2D context");const t=p;let h=0,m=!0;function y(){const e=c.width,a=c.height;return{x:Math.random()*e,y:Math.random()*a,vx:(Math.random()-.5)*d*1.5,vy:(Math.random()-.5)*d*1.5,color:u[Math.floor(Math.random()*u.length)],size:x*(.5+Math.random()*.8),alpha:Math.random(),alphaDir:Math.random()>.5?1:-1}}const i=Array.from({length:o},y);function S(e){const a=c.width,r=c.height;e.x+=e.vx*d,e.y+=e.vy*d,(e.x<0||e.x>a)&&(e.vx*=-1),(e.y<0||e.y>r)&&(e.vy*=-1),e.alpha+=.012*e.alphaDir,e.alpha>=1&&(e.alpha=1,e.alphaDir=-1),e.alpha<=.1&&(e.alpha=.1,e.alphaDir=1)}function z(e){t.globalAlpha=e.alpha,t.fillStyle=e.color,t.fillRect(Math.round(e.x),Math.round(e.y),Math.round(e.size),Math.round(e.size))}function C(){for(let a=0;a<i.length;a++)for(let r=a+1;r<i.length;r++){const M=i[a].x-i[r].x,_=i[a].y-i[r].y,P=Math.sqrt(M*M+_*_);P<80&&(t.globalAlpha=(1-P/80)*.25,t.strokeStyle=i[a].color,t.lineWidth=.8,t.beginPath(),t.moveTo(i[a].x,i[a].y),t.lineTo(i[r].x,i[r].y),t.stroke())}}function v(){if(!m)return;const e=c.width,a=c.height;t.globalAlpha=.18,t.fillStyle="#000",t.fillRect(0,0,e,a),t.globalAlpha=1;for(const r of i)S(r),z(r);C(),l==null||l(),h=requestAnimationFrame(v)}return v(),function(){m=!1,cancelAnimationFrame(h)}}const b=["width","height"],g=((f,c)=>{const u=f.__vccOpts||f;for(const[o,d]of c)u[o]=d;return u})(n.defineComponent({__name:"PixelFlow",props:{width:{default:600},height:{default:400},colors:{default:()=>["#00f5ff","#7b2fff","#ff2fa0","#ffdd00"]},particleCount:{default:120},speed:{default:1},pixelSize:{default:4},background:{default:"#0d0d0d"},autoPlay:{type:Boolean,default:!0}},emits:["ready","tick"],setup(f,{expose:c,emit:u}){const o=f,d=u,x=n.ref(null);let l=null,p=0;const t=n.computed(()=>({display:"block",background:o.background}));function h(){if(!x.value)return;l==null||l();const y={canvas:x.value,colors:o.colors,particleCount:o.particleCount,speed:o.speed,pixelSize:o.pixelSize,onTick:()=>d("tick",++p)};l=w(y),d("ready")}function m(){l==null||l(),l=null}return n.onMounted(()=>{o.autoPlay&&h()}),n.onUnmounted(()=>{m()}),n.watch(()=>[o.particleCount,o.speed,o.pixelSize,o.colors],()=>{l&&h()},{deep:!0}),c({start:h,stop:m}),(y,i)=>(n.openBlock(),n.createElementBlock("canvas",{ref_key:"canvasRef",ref:x,width:f.width,height:f.height,style:n.normalizeStyle(t.value),class:"pixel-flow-canvas"},null,12,b))}}),[["__scopeId","data-v-89863d06"]]),k={install(f){f.component("PixelFlow",g)}};s.PixelFlow=g,s.createPixelFlow=w,s.default=k,Object.defineProperties(s,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
2
|
+
//# sourceMappingURL=pixel-flow-vue.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixel-flow-vue.umd.cjs","sources":["../src/core/pixelFlow.ts","../src/components/PixelFlow.vue","../src/index.ts"],"sourcesContent":["import type { PixelFlowOptions } from '../types'\n\n// -------------------------------------------------------\n// 粒子结构\n// -------------------------------------------------------\ninterface Particle {\n x: number\n y: number\n vx: number\n vy: number\n color: string\n size: number\n alpha: number\n alphaDir: number\n}\n\n// -------------------------------------------------------\n// 工厂函数:createPixelFlow\n// 返回一个 stop 函数,调用后停止动画循环\n// -------------------------------------------------------\nexport function createPixelFlow(options: PixelFlowOptions): () => void {\n const {\n canvas,\n colors = ['#00f5ff', '#7b2fff', '#ff2fa0', '#ffdd00'],\n particleCount = 120,\n speed = 1,\n pixelSize = 4,\n onTick,\n } = options\n\n const rawCtx = canvas.getContext('2d')\n if (!rawCtx) throw new Error('[pixel-flow-vue] Failed to get 2D context')\n // After the null-check, rebind as non-nullable so TypeScript is satisfied\n // inside every nested closure (TS cannot narrow across function boundaries).\n const ctx: CanvasRenderingContext2D = rawCtx\n\n let rafId = 0\n let running = true\n\n // ---- 初始化粒子 ----\n function makeParticle(): Particle {\n const w = canvas.width\n const h = canvas.height\n return {\n x: Math.random() * w,\n y: Math.random() * h,\n vx: (Math.random() - 0.5) * speed * 1.5,\n vy: (Math.random() - 0.5) * speed * 1.5,\n color: colors[Math.floor(Math.random() * colors.length)],\n size: pixelSize * (0.5 + Math.random() * 0.8),\n alpha: Math.random(),\n alphaDir: Math.random() > 0.5 ? 1 : -1,\n }\n }\n\n const particles: Particle[] = Array.from({ length: particleCount }, makeParticle)\n\n // ---- 更新单个粒子 ----\n function updateParticle(p: Particle): void {\n const w = canvas.width\n const h = canvas.height\n\n p.x += p.vx * speed\n p.y += p.vy * speed\n\n // 边界回弹\n if (p.x < 0 || p.x > w) p.vx *= -1\n if (p.y < 0 || p.y > h) p.vy *= -1\n\n // 呼吸透明度\n p.alpha += 0.012 * p.alphaDir\n if (p.alpha >= 1) { p.alpha = 1; p.alphaDir = -1 }\n if (p.alpha <= 0.1) { p.alpha = 0.1; p.alphaDir = 1 }\n }\n\n // ---- 绘制单个像素块 ----\n function drawParticle(p: Particle): void {\n ctx.globalAlpha = p.alpha\n ctx.fillStyle = p.color\n ctx.fillRect(\n Math.round(p.x),\n Math.round(p.y),\n Math.round(p.size),\n Math.round(p.size),\n )\n }\n\n // ---- 连线:距离近的粒子之间画线 ----\n function drawConnections(): void {\n const maxDist = 80\n for (let i = 0; i < particles.length; i++) {\n for (let j = i + 1; j < particles.length; j++) {\n const dx = particles[i].x - particles[j].x\n const dy = particles[i].y - particles[j].y\n const dist = Math.sqrt(dx * dx + dy * dy)\n if (dist < maxDist) {\n ctx.globalAlpha = (1 - dist / maxDist) * 0.25\n ctx.strokeStyle = particles[i].color\n ctx.lineWidth = 0.8\n ctx.beginPath()\n ctx.moveTo(particles[i].x, particles[i].y)\n ctx.lineTo(particles[j].x, particles[j].y)\n ctx.stroke()\n }\n }\n }\n }\n\n // ---- 动画主循环 ----\n function loop(): void {\n if (!running) return\n\n const w = canvas.width\n const h = canvas.height\n\n // 拖尾效果:半透明清屏\n ctx.globalAlpha = 0.18\n ctx.fillStyle = '#000'\n ctx.fillRect(0, 0, w, h)\n\n ctx.globalAlpha = 1\n\n // 更新 & 绘制\n for (const p of particles) {\n updateParticle(p)\n drawParticle(p)\n }\n\n drawConnections()\n\n onTick?.()\n rafId = requestAnimationFrame(loop)\n }\n\n loop()\n\n // ---- 返回 stop 函数 ----\n return function stop() {\n running = false\n cancelAnimationFrame(rafId)\n }\n}\n","<template>\n <canvas\n ref=\"canvasRef\"\n :width=\"width\"\n :height=\"height\"\n :style=\"canvasStyle\"\n class=\"pixel-flow-canvas\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, computed, watch } from 'vue'\nimport type { CSSProperties } from 'vue'\nimport { createPixelFlow } from '../core/pixelFlow'\nimport type { PixelFlowOptions } from '../types'\n\n// ----------------------------------------------------------------\n// Props\n// ----------------------------------------------------------------\ninterface Props {\n /** 画布宽度,默认 600 */\n width?: number\n /** 画布高度,默认 400 */\n height?: number\n /** 像素粒子颜色列表 */\n colors?: string[]\n /** 粒子数量,默认 120 */\n particleCount?: number\n /** 粒子速度倍率 (0.1 ~ 5),默认 1 */\n speed?: number\n /** 像素大小,默认 4 */\n pixelSize?: number\n /** 背景色,默认 '#0d0d0d' */\n background?: string\n /** 是否自动播放,默认 true */\n autoPlay?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n width: 600,\n height: 400,\n colors: () => ['#00f5ff', '#7b2fff', '#ff2fa0', '#ffdd00'],\n particleCount: 120,\n speed: 1,\n pixelSize: 4,\n background: '#0d0d0d',\n autoPlay: true,\n})\n\n// ----------------------------------------------------------------\n// Emits\n// ----------------------------------------------------------------\nconst emit = defineEmits<{\n (e: 'ready'): void\n (e: 'tick', frame: number): void\n}>()\n\n// ----------------------------------------------------------------\n// Refs & state\n// ----------------------------------------------------------------\nconst canvasRef = ref<HTMLCanvasElement | null>(null)\nlet stopFn: (() => void) | null = null\nlet frame = 0\n\nconst canvasStyle = computed<CSSProperties>(() => ({\n display: 'block',\n background: props.background,\n}))\n\n// ----------------------------------------------------------------\n// Start / Stop helpers\n// ----------------------------------------------------------------\nfunction start() {\n if (!canvasRef.value) return\n stopFn?.()\n\n const options: PixelFlowOptions = {\n canvas: canvasRef.value,\n colors: props.colors,\n particleCount: props.particleCount,\n speed: props.speed,\n pixelSize: props.pixelSize,\n onTick: () => emit('tick', ++frame),\n }\n\n stopFn = createPixelFlow(options)\n emit('ready')\n}\n\nfunction stop() {\n stopFn?.()\n stopFn = null\n}\n\n// ----------------------------------------------------------------\n// Lifecycle\n// ----------------------------------------------------------------\nonMounted(() => {\n if (props.autoPlay) start()\n})\n\nonUnmounted(() => {\n stop()\n})\n\nwatch(\n () => [props.particleCount, props.speed, props.pixelSize, props.colors],\n () => {\n if (stopFn) start() // 重启以应用新配置\n },\n { deep: true }\n)\n\n// ----------------------------------------------------------------\n// Expose public API\n// ----------------------------------------------------------------\ndefineExpose({ start, stop })\n</script>\n\n<style scoped>\n.pixel-flow-canvas {\n border-radius: 4px;\n}\n</style>\n","import type { App } from 'vue'\nimport PixelFlow from './components/PixelFlow.vue'\nexport type { PixelFlowOptions, PixelFlowInstance } from './types'\nexport { createPixelFlow } from './core/pixelFlow'\n\n// ----------------------------------------------------------------\n// Vue 插件安装函数\n// ----------------------------------------------------------------\nconst PixelFlowVue = {\n install(app: App) {\n app.component('PixelFlow', PixelFlow)\n },\n}\n\n// ----------------------------------------------------------------\n// 导出\n// ----------------------------------------------------------------\nexport { PixelFlow }\nexport default PixelFlowVue\n"],"names":["createPixelFlow","options","canvas","colors","particleCount","speed","pixelSize","onTick","rawCtx","ctx","rafId","running","makeParticle","w","h","particles","updateParticle","p","drawParticle","drawConnections","i","j","dx","dy","dist","loop","props","__props","emit","__emit","canvasRef","ref","stopFn","frame","canvasStyle","computed","start","stop","onMounted","onUnmounted","watch","__expose","_createElementBlock","PixelFlowVue","app","PixelFlow"],"mappings":"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"}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.pixel-flow-canvas[data-v-89863d06]{border-radius:4px}
|
package/package.json
CHANGED
|
@@ -1,45 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixel-flow-vue",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A Vue
|
|
3
|
+
"version": "1.0.10",
|
|
4
|
+
"description": "A lightweight Vue 3 pixel-flow animation plugin with TypeScript support",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/pixel-flow.umd.
|
|
7
|
-
"module": "./dist/pixel-flow.
|
|
8
|
-
"types": "./dist/
|
|
6
|
+
"main": "./dist/pixel-flow-vue.umd.cjs",
|
|
7
|
+
"module": "./dist/pixel-flow-vue.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"import": "./dist/pixel-flow.
|
|
12
|
-
"require": "./dist/pixel-flow.umd.
|
|
13
|
-
|
|
11
|
+
"import": "./dist/pixel-flow-vue.js",
|
|
12
|
+
"require": "./dist/pixel-flow-vue.umd.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": "./dist/style.css"
|
|
14
16
|
},
|
|
15
17
|
"files": [
|
|
16
18
|
"dist",
|
|
17
|
-
"README.md"
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
18
21
|
],
|
|
19
22
|
"scripts": {
|
|
23
|
+
"dev": "vite",
|
|
20
24
|
"build": "vue-tsc --noEmit && vite build",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"vue": "^3.2.0 || ^3.3.0 || ^3.4.0"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@vitejs/plugin-vue": "^5.0.0",
|
|
28
|
-
"typescript": "^5.3.0",
|
|
29
|
-
"vite": "^5.0.0",
|
|
30
|
-
"vue": "^3.4.0",
|
|
31
|
-
"vue-tsc": "^1.8.0"
|
|
25
|
+
"build:types": "vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
26
|
+
"preview": "vite preview",
|
|
27
|
+
"lint": "eslint src --ext .ts,.vue --fix"
|
|
32
28
|
},
|
|
33
29
|
"keywords": [
|
|
30
|
+
"vue",
|
|
34
31
|
"vue3",
|
|
35
|
-
"
|
|
32
|
+
"plugin",
|
|
33
|
+
"pixel",
|
|
34
|
+
"flow",
|
|
35
|
+
"animation",
|
|
36
|
+
"canvas",
|
|
36
37
|
"typescript",
|
|
37
|
-
"
|
|
38
|
+
"component"
|
|
38
39
|
],
|
|
39
40
|
"author": "",
|
|
40
41
|
"license": "MIT",
|
|
41
42
|
"repository": {
|
|
42
43
|
"type": "git",
|
|
43
|
-
"url": ""
|
|
44
|
+
"url": "https://github.com/your-username/pixel-flow-vue.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/your-username/pixel-flow-vue#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/your-username/pixel-flow-vue/issues"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"vue": "^3.3.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.14.0",
|
|
55
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
56
|
+
"typescript": "^5.4.0",
|
|
57
|
+
"vite": "^5.3.0",
|
|
58
|
+
"vite-plugin-dts": "^3.9.0",
|
|
59
|
+
"vue": "^3.4.0",
|
|
60
|
+
"vue-tsc": "^2.0.0"
|
|
44
61
|
}
|
|
45
|
-
}
|
|
62
|
+
}
|
package/dist/pixel-flow-vue.css
DELETED
package/dist/pixel-flow.es.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createElementBlock as e, createTextVNode as t, defineComponent as n, normalizeStyle as r, openBlock as i, renderSlot as a } from "vue";
|
|
2
|
-
//#endregion
|
|
3
|
-
//#region src/components/PixelFlow.vue
|
|
4
|
-
var o = /*#__PURE__*/ ((e, t) => {
|
|
5
|
-
let n = e.__vccOpts || e;
|
|
6
|
-
for (let [e, r] of t) n[e] = r;
|
|
7
|
-
return n;
|
|
8
|
-
})(/* @__PURE__ */ n({
|
|
9
|
-
__name: "PixelFlow",
|
|
10
|
-
props: { color: {} },
|
|
11
|
-
setup(n) {
|
|
12
|
-
return (o, s) => (i(), e("div", {
|
|
13
|
-
class: "pixel-flow",
|
|
14
|
-
style: r({ color: n.color })
|
|
15
|
-
}, [a(o.$slots, "default", {}, () => [s[0] ||= t("默认内容", -1)], !0)], 4));
|
|
16
|
-
}
|
|
17
|
-
}), [["__scopeId", "data-v-61417c41"]]), s = { install(e, t = {}) {
|
|
18
|
-
e.component("PixelFlow", o), t.defaultColor && console.log("全局默认颜色:", t.defaultColor);
|
|
19
|
-
} };
|
|
20
|
-
typeof window < "u" && window.Vue && window.Vue.use(s, {});
|
|
21
|
-
//#endregion
|
|
22
|
-
export { o as PixelFlow, s as default };
|
package/dist/pixel-flow.umd.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.PixelFlowVue3={},e.Vue))})(this,function(e,t){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var n=((e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n})((0,t.defineComponent)({__name:`PixelFlow`,props:{color:{}},setup(e){return(n,r)=>((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{class:`pixel-flow`,style:(0,t.normalizeStyle)({color:e.color})},[(0,t.renderSlot)(n.$slots,`default`,{},()=>[r[0]||=(0,t.createTextVNode)(`默认内容`,-1)],!0)],4))}}),[[`__scopeId`,`data-v-61417c41`]]),r={install(e,t={}){e.component(`PixelFlow`,n),t.defaultColor&&console.log(`全局默认颜色:`,t.defaultColor)}};typeof window<`u`&&window.Vue&&window.Vue.use(r,{}),e.PixelFlow=n,e.default=r});
|