feat: 🚀 图形;线段;动画

This commit is contained in:
bunny 2024-07-08 12:18:48 +08:00
parent 0d03844090
commit 1daa0d7540
23 changed files with 376 additions and 131 deletions

View File

@ -37,6 +37,7 @@
"@vitejs/plugin-legacy": "^5.4.0",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue2-jsx": "^1.1.1",
"@vueuse/core": "^10.11.0",
"autoprefixer": "^10.4.19",
"axios": "^1.6.7",
"boxen": "^7.1.1",
@ -47,6 +48,7 @@
"dotenv": "^16.4.5",
"echarts": "^5.5.0",
"gradient-string": "^2.0.2",
"konva": "^9.3.13",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"moment": "^2.30.1",

View File

@ -20,6 +20,9 @@ importers:
'@vitejs/plugin-vue2-jsx':
specifier: ^1.1.1
version: 1.1.1(rollup@4.18.0)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.3.3))
'@vueuse/core':
specifier: ^10.11.0
version: 10.11.0(vue@3.4.31(typescript@5.3.3))
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.39)
@ -50,6 +53,9 @@ importers:
gradient-string:
specifier: ^2.0.2
version: 2.0.2
konva:
specifier: ^9.3.13
version: 9.3.13
lodash:
specifier: ^4.17.21
version: 4.17.21
@ -1467,6 +1473,9 @@ packages:
'@types/uuid@9.0.8':
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
'@types/web-bluetooth@0.0.20':
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
'@types/webpack-env@1.18.5':
resolution: {integrity: sha512-wz7kjjRRj8/Lty4B+Kr0LN6Ypc/3SymeCCGSbaXp2leH0ZVg/PriNiOwNj4bD4uphI7A8NXS4b6Gl373sfO5mA==}
@ -1833,6 +1842,15 @@ packages:
'@vue/web-component-wrapper@1.3.0':
resolution: {integrity: sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==}
'@vueuse/core@10.11.0':
resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==}
'@vueuse/metadata@10.11.0':
resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==}
'@vueuse/shared@10.11.0':
resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==}
'@webassemblyjs/ast@1.12.1':
resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
@ -3812,6 +3830,9 @@ packages:
known-css-properties@0.31.0:
resolution: {integrity: sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==}
konva@9.3.13:
resolution: {integrity: sha512-hs0ysHnqjK9noZ/rkfDNJINfbNhkXMgjgkJ8uc6vU0amu05mSDtRlukz5kKHOaSnWHA6miXcHJydvPABh18Y8A==}
launch-editor-middleware@2.8.0:
resolution: {integrity: sha512-0Az27jnPR2RgkUoZoLHluM5gg9zHeg7hPsUZESJxcTV8Rs6Fed+Nof7Lb2HmpsE8lN/3YzpU+mvK5exYWSftWw==}
@ -7233,6 +7254,8 @@ snapshots:
'@types/uuid@9.0.8': {}
'@types/web-bluetooth@0.0.20': {}
'@types/webpack-env@1.18.5': {}
'@types/ws@8.5.10':
@ -7957,6 +7980,25 @@ snapshots:
'@vue/web-component-wrapper@1.3.0': {}
'@vueuse/core@10.11.0(vue@3.4.31(typescript@5.3.3))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.11.0
'@vueuse/shared': 10.11.0(vue@3.4.31(typescript@5.3.3))
vue-demi: 0.14.8(vue@3.4.31(typescript@5.3.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
'@vueuse/metadata@10.11.0': {}
'@vueuse/shared@10.11.0(vue@3.4.31(typescript@5.3.3))':
dependencies:
vue-demi: 0.14.8(vue@3.4.31(typescript@5.3.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
'@webassemblyjs/ast@1.12.1':
dependencies:
'@webassemblyjs/helper-numbers': 1.11.6
@ -9912,6 +9954,8 @@ snapshots:
known-css-properties@0.31.0: {}
konva@9.3.13: {}
launch-editor-middleware@2.8.0:
dependencies:
launch-editor: 2.8.0

View File

@ -1 +1 @@
{"version":1720007747647}
{"version":1720401846729}

View File

@ -1,24 +0,0 @@
<template>
<textarea type="text" v-model="myData.inputValue" />
<button v-copy="myData.copyValue" @click="handelClik">点击复制</button>
<button v-vPrint>点击打印</button>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const myData = reactive({
inputValue: '我是被复制的内容 🍒🍇🍊2023-4-21 14:02:59',
copyValue: '',
});
const handelClik = () => {
myData.copyValue = myData.inputValue;
};
</script>
<style scoped lang="scss">
textarea {
width: 500px;
height: 300px;
outline: 1px solid #f4dde5;
}
</style>

View File

@ -1,15 +0,0 @@
<template>
<div v-waterMarker="{ text: 'Bunny Admin', textColor: 'rgba(180, 180, 180, 0.6)' }" v-existShow>
<h1 @click="handelClik">bunny/default页面</h1>
<My />
<RouterView />
</div>
</template>
<script setup lang="ts">
import My from '@/views/bunny/default/My.vue';
const handelClik = () => {
print();
};
</script>

View File

@ -1,6 +0,0 @@
export default {
meta: {
title: '',
},
name: 'aaa',
};

View File

@ -1,3 +0,0 @@
<template>aChildren.vue</template>
<script setup lang="ts"></script>
<style scoped lang="scss"></style>

View File

@ -1,3 +0,0 @@
<template>B的路由</template>
<script setup lang="ts"></script>
<style scoped lang="scss"></style>

View File

@ -1,10 +0,0 @@
<template>
A的路由45465456456
<AChildren />
<RouterView />
</template>
<script setup lang="ts">
import AChildren from './aChildren.vue';
</script>
<style scoped lang="scss"></style>

View File

@ -1,6 +0,0 @@
<template>
<RouterView />
<h1>bunny/my 页面</h1>
</template>
<script setup lang="ts"></script>

View File

@ -1,15 +0,0 @@
const route = {
name: 'my-route',
meta: { isFullScreen: true },
children: [
{ name: 'bunny_my_a', path: '/bunny/my/a', component: () => import('@/views/bunny/www/index.vue') },
{
name: 'bunny_my_a111',
path: '/bunny/my/a/children',
component: () => import('@/views/bunny/my/a/aChildren.vue'),
},
{ name: 'bunny_my_a_b', path: '/bunny/my/a/b', component: () => import('@/views/bunny/my/a/b/index.vue') },
],
};
export default route;

View File

@ -1 +0,0 @@
<template>www</template>

View File

@ -1,6 +0,0 @@
<template>
<h1>about页面</h1>
<RouterView />
</template>
<script setup lang="ts"></script>

View File

@ -1,6 +0,0 @@
<template>
<h1>common页面</h1>
<RouterView />
</template>
<script setup lang="ts"></script>

View File

@ -1,6 +0,0 @@
<template>
<h1>home页面</h1>
<RouterView />
</template>
<script setup lang="ts"></script>

View File

@ -1,3 +0,0 @@
export default {
name: 'bunny',
};

View File

@ -1,30 +1,19 @@
<template>
<div class="container-fluid">
<h1>vite-pinia 模板</h1>
<h1>多语言{{ $t('tip.loading') }}</h1>
<div class="row">
<button class="btn btn-primary col-3 me-1" @click="handleI18n('en')">切换英文</button>
<button class="btn btn-primary col-3 mr-2" @click="handleI18n('zh_cn')">切换中文</button>
<button class="btn btn-success col-3" @click="fetchMockRequest()">发送请求(没有接口)</button>
</div>
<ul>
<li v-for="(route, index) in pageRoutes" :key="index">
<RouterLink :to="route.path">{{ `${route.path}${String(route.name)}` }}</RouterLink>
</li>
</ul>
<Test />
<RouterView />
</div>
</template>
<script lang="ts" setup>
import Test from '@/views/test-tsx/index.tsx';
import { pageRoutes } from '@/router/module/pageRoutes';
import { userI18nStore } from '@/store/i18n.ts';
import { useI18n } from 'vue-i18n';
import { fetchTestApi } from '@/api/api-v1/test.ts';
const i18nStore = userI18nStore();
const i18n = useI18n();
@ -37,14 +26,6 @@ const handleI18n = (value: string) => {
i18n.locale.value = value;
i18n.mergeLocaleMessage(value, i18nStore.i18n[value]);
};
/**
* * 模拟简单的请求--做测试用
*/
const fetchMockRequest = async () => {
const response = await fetchTestApi();
console.log(response);
};
</script>
<style lang="scss" scoped></style>

98
src/views/line/index.vue Normal file
View File

@ -0,0 +1,98 @@
<script setup lang="ts">
import { nextTick, onBeforeMount } from 'vue';
import { Stage } from 'konva/lib/Stage';
import { useWindowSize } from '@vueuse/core';
import { Layer } from 'konva/lib/Layer';
import { Line } from 'konva/lib/shapes/Line';
const { width, height } = useWindowSize();
const initial = () => {
const stage = new Stage({
container: 'container',
width: width.value,
height: height.value,
});
const layer = new Layer({ draggable: true });
const line1 = new Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
x: 0,
y: 0,
stroke: 'blue',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round',
});
layer.add(line1);
const line2 = new Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 3,
lineCap: 'square',
dash: [33, 10],
});
layer.add(line2);
line2.move({ x: 0, y: 10 });
const line3 = new Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 3,
lineCap: 'square',
dash: [29, 20, 0.001, 20],
});
layer.add(line3);
line3.move({ x: 0, y: 20 });
const line4 = new Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true,
});
layer.add(line4);
line4.move({ x: 0, y: 80 });
const line5 = new Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 10,
lineJoin: 'round',
lineCap: 'round',
dash: [33, 26],
tension: 0.5,
});
layer.add(line5);
line5.move({ x: 0, y: 220 });
const line6 = new Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 10,
lineJoin: 'round',
lineCap: 'round',
dash: [33, 26],
fill: 'red',
tension: 0.5,
closed: true,
});
layer.add(line6);
line6.move({ x: 0, y: 300 });
stage.add(layer);
};
onBeforeMount(() => {
nextTick(() => initial());
});
</script>
<template>
<div id="container"></div>
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,50 @@
<template>
<div>
<div id="container"></div>
</div>
</template>
<script setup lang="ts">
import { Layer } from 'konva/lib/Layer';
import { Circle } from 'konva/lib/shapes/Circle';
import { Stage } from 'konva/lib/Stage';
import { nextTick, onBeforeMount } from 'vue';
const initial = () => {
// first we need to create a stage
var stage = new Stage({
container: 'container', // id of container <div>
width: 500,
height: 500,
});
// then create layer
var layer = new Layer();
// create our shape
var circle = new Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
// add the shape to the layer
layer.add(circle);
// add the layer to the stage
stage.add(layer);
stage.draggable(true);
// draw the image
layer.draw();
};
onBeforeMount(() => {
nextTick(() => {
initial();
});
});
</script>

View File

@ -0,0 +1,8 @@
<template>
<Rect />
<hr />
</template>
<script setup lang="ts">
import Rect from '@/views/shape/rect.vue';
</script>

105
src/views/shape/rect.vue Normal file
View File

@ -0,0 +1,105 @@
<template>
<div id="container"></div>
</template>
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core';
import { Layer } from 'konva/lib/Layer';
import { Rect } from 'konva/lib/shapes/Rect';
import { Stage } from 'konva/lib/Stage';
import { nextTick, onBeforeMount } from 'vue';
import { Circle } from 'konva/lib/shapes/Circle';
import { Ellipse } from 'konva/lib/shapes/Ellipse';
import { Wedge } from 'konva/lib/shapes/Wedge';
const { width, height } = useWindowSize();
const initial = async () => {
//
const stage = new Stage({
container: 'container',
width: width.value / 2,
height: height.value / 2,
});
//
const layer = new Layer({ draggable: true });
const rect1 = new Rect({
x: 10,
y: 10,
width: 100,
height: 100,
fill: '#824393',
stroke: '#ff',
strokeWidth: 2,
});
layer.add(rect1);
const rect2 = new Rect({
x: 200,
y: 10,
width: 100,
height: 100,
fill: 'green',
stroke: '#000',
strokeWidth: 2,
cornerRadius: 10,
});
layer.add(rect2);
const rect3 = new Rect({
x: 400,
y: 10,
width: 100,
height: 100,
fill: 'green',
stroke: '#000',
strokeWidth: 2,
cornerRadius: [0, 10, 20, 30],
draggable: true,
});
layer.add(rect3);
const layer1 = new Layer({ draggable: true });
const circle1 = new Circle({
x: 60,
y: 200,
radius: 50,
fill: 'blue',
stroke: '#000',
strokeWidth: 2,
});
layer1.add(circle1);
const ellipse = new Ellipse({
x: 220,
y: 200,
radiusX: 100,
radiusY: 50,
fill: 'green',
stroke: '#000',
strokeWidth: 2,
});
layer1.add(ellipse);
const wedge = new Wedge({
x: 360,
y: 200,
radius: 70,
angle: 30,
stroke: 'green',
strokeWidth: 3,
fill: 'red',
rotation: -60,
});
layer1.add(wedge);
stage.add(layer);
stage.add(layer1);
};
onBeforeMount(() => {
nextTick(() => initial());
});
</script>

View File

@ -0,0 +1,68 @@
<script setup lang="ts">
import { nextTick, onBeforeMount, ref } from 'vue';
import { Stage } from 'konva/lib/Stage';
import { Layer } from 'konva/lib/Layer';
import { Sprite } from 'konva/lib/shapes/Sprite';
const blob = ref();
const initial = () => {
const stage = new Stage({
width: 500,
height: 500,
container: 'container',
});
const layer = new Layer({ draggable: true });
const animations = {
idle: [
// x, y, width, height (4 frames)
2, 2, 70, 119, 71, 2, 74, 119, 146, 2, 81, 119, 226, 2, 76, 119,
],
punch: [
// x, y, width, height (4 frames)
2, 138, 74, 122, 76, 138, 84, 122, 346, 138, 120, 122,
],
};
const imageObj = new Image();
imageObj.onload = function () {
blob.value = new Sprite({
x: 50,
y: 50,
image: imageObj,
animation: 'idle',
animations: animations,
frameRate: 7,
frameIndex: 0,
});
layer.add(blob.value);
blob.value.start();
};
imageObj.src = 'https://konvajs.org/assets/blob-sprite.png';
stage.add(layer);
};
const onClickImage = () => {
blob.value.animation('punch');
blob.value.on('frameIndexChange.button', function () {
if (this.frameIndex() === 2) {
setTimeout(function () {
blob.value.animation('idle');
blob.value.off('.button');
}, 1000 / blob.value.frameRate());
}
});
};
onBeforeMount(() => {
nextTick(() => initial());
});
</script>
<template>
<button class="btn btn-primary" @click="onClickImage">点击</button>
<div id="container"></div>
</template>
<style scoped lang="scss"></style>

View File

@ -1,7 +0,0 @@
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
return () => <h1> </h1>;
},
});