page: 📄 横向和垂直移动
327
README.md
|
@ -1,16 +1,4 @@
|
|||
# 待学习内容
|
||||
|
||||
```sh
|
||||
Remove by Name
|
||||
Listen for Events
|
||||
Cancel Propagation
|
||||
Event Delegation
|
||||
Fire Events
|
||||
Stage Events
|
||||
Custom Hit Region
|
||||
Image Hit Region
|
||||
Keyboard Events
|
||||
Desktop and Mobile
|
||||
Simple Drag Bounds
|
||||
Complex Drag and Drop
|
||||
Drop Events
|
||||
|
@ -89,317 +77,4 @@ Shape Redraw
|
|||
Disable Perfect Drawing
|
||||
Listening False
|
||||
Avoid Memory Leaks
|
||||
```
|
||||
|
||||
# Vite相关使用
|
||||
|
||||
## .env文件
|
||||
|
||||
### 文件名简介
|
||||
|
||||
- `.env`:公用配置文件
|
||||
- `.env.development`:开发环境配置
|
||||
- `.env.production`:生产话就配置
|
||||
|
||||
### 修改环境前缀
|
||||
|
||||
在配置文件中加上`envPrefix`
|
||||
|
||||
```ts
|
||||
export default defineConfig(
|
||||
(): UserConfig => ({
|
||||
envPrefix: 'BUNNY',
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
## 使用gzip
|
||||
|
||||
安装
|
||||
|
||||
```sh
|
||||
npm i vite-plugin-compress
|
||||
```
|
||||
|
||||
需要文件中引入
|
||||
|
||||
```js
|
||||
import compress from 'vite-plugin-compress';
|
||||
|
||||
export default {
|
||||
plugins: [compress()],
|
||||
};
|
||||
```
|
||||
|
||||
## 动态加载路由
|
||||
|
||||
```ts
|
||||
import { routeFilenameHelper } from '@/utils/file/routeFileUtil';
|
||||
import _ from 'lodash';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
// * 最终路由
|
||||
const routeMap: Record<string, RouteRecordRaw> = {};
|
||||
|
||||
// * 所有处理的路由
|
||||
const contexts = [
|
||||
{ context: import.meta.glob('@/views/**/index.vue', { eager: true, import: 'default' }), isIndex: true },
|
||||
{ context: import.meta.glob('@/views/**/page.ts', { eager: true, import: 'default' }), isIndex: false },
|
||||
];
|
||||
|
||||
/**
|
||||
* 构建路由信息
|
||||
* @param context 上下文信息
|
||||
* @param isIndex 是否第一次遍历
|
||||
* @param route 路由内容
|
||||
*/
|
||||
function buildRouteTree(context: any, isIndex: boolean, route: any) {
|
||||
// 遍历当前子路由
|
||||
Object.entries(context).forEach(([filePath, _]) => {
|
||||
// 获取子路由下所有文件对象格式
|
||||
const childrenFileInfo = routeFilenameHelper(filePath);
|
||||
// 组装子路由对象
|
||||
const childrenRoute: any = {
|
||||
name: childrenFileInfo?.name,
|
||||
path: childrenFileInfo!.path,
|
||||
component: isIndex ? context[filePath] : undefined,
|
||||
children: [],
|
||||
meta: { isFullScreen: false },
|
||||
};
|
||||
// 如果当前路由对象等于当前遍历的路由子对象,将子路由推到父级路由中
|
||||
if (childrenFileInfo?.path.includes(route.path) && childrenFileInfo?.path !== route.path) {
|
||||
route.children.push(childrenRoute);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历路由信息
|
||||
* @param context 路由上下文
|
||||
* @param isIndex 是否为索引遍历
|
||||
*/
|
||||
const createRouteList = (context: any, isIndex: boolean) => {
|
||||
Object.entries(context).forEach(([filePath, exportRouteConfig]) => {
|
||||
const fileInfo = routeFilenameHelper(filePath);
|
||||
// 组装路由对象
|
||||
const route: any = {
|
||||
name: fileInfo?.name,
|
||||
path: fileInfo!.path,
|
||||
component: isIndex ? context[filePath] : undefined,
|
||||
children: [],
|
||||
meta: { isFullScreen: false },
|
||||
};
|
||||
|
||||
// 初始化赋值
|
||||
if (isIndex) {
|
||||
routeMap[route.path] = route;
|
||||
buildRouteTree(context, isIndex, route);
|
||||
} else {
|
||||
// 导出当前存在的路由并重新赋值
|
||||
const existingRoute = routeMap[route.path];
|
||||
// 当前路由存在
|
||||
if (existingRoute) {
|
||||
// 使用loadsh合并对象
|
||||
routeMap[route.path] = _.merge(existingRoute, exportRouteConfig);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// * 生成路由信息
|
||||
contexts.forEach(({ context, isIndex }) => createRouteList(context, isIndex));
|
||||
|
||||
export const pageRoutes: Array<RouteRecordRaw> = Object.values(routeMap);
|
||||
```
|
||||
|
||||
> 需要注意的是,为了动态导入生产环境也可以运行,需要`context[filePath]`需要使用这段话,才可以
|
||||
|
||||
## 输出环境变量内容
|
||||
|
||||
```ts
|
||||
console.log(import.meta.env);
|
||||
```
|
||||
|
||||
## 动态加载CDN
|
||||
|
||||
在Module中配置相关内容,相关文档查看:<https://www.npmjs.com/package/vite-plugin-cdn-import>
|
||||
|
||||
```ts
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, UserConfig } from 'vite';
|
||||
import cdn from 'vite-plugin-cdn-import';
|
||||
|
||||
export default defineConfig(
|
||||
(): UserConfig => ({
|
||||
plugins: [
|
||||
cdn({
|
||||
modules: [],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
> ```
|
||||
> npm i vite-plugin-cdn-import
|
||||
> ```
|
||||
|
||||
## 兼容老版本浏览器
|
||||
|
||||
在plugin中加入
|
||||
|
||||
```ts
|
||||
legacy({ targets: ["defaults", "not IE 11"] }),
|
||||
```
|
||||
|
||||
详细配置查看:<https://www.npmjs.com/package/@vitejs/plugin-legacy>
|
||||
|
||||
## 打包设置
|
||||
|
||||
如果打包中包含`node_module`包全部放到`vendor-[hash].js`中
|
||||
|
||||

|
||||
|
||||
## 配置跨域
|
||||
|
||||
`process.env.BUNNY_APP_URL`设置在这三个文件中,读取是这三个配置
|
||||
|
||||

|
||||
|
||||
```ts
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port
|
||||
:
|
||||
6261,
|
||||
open
|
||||
:
|
||||
true,
|
||||
cors
|
||||
:
|
||||
true,
|
||||
proxy
|
||||
:
|
||||
{
|
||||
"/api"
|
||||
:
|
||||
{
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin
|
||||
:
|
||||
true,
|
||||
rewrite
|
||||
:
|
||||
(path: string) => path.replace(/^\/api/, "/api")
|
||||
}
|
||||
,
|
||||
"/mock"
|
||||
:
|
||||
{
|
||||
target: process.env.BUNNY_APP_URL,
|
||||
changeOrigin
|
||||
:
|
||||
true,
|
||||
rewrite
|
||||
:
|
||||
(path: string) => path.replace(/^\/mock/, "/mock")
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
```
|
||||
|
||||
## 配置JSX
|
||||
|
||||
项目中已经设置好了JSX相关功能
|
||||
|
||||

|
||||
|
||||
## Mock和多语言
|
||||
|
||||
### Mock设置
|
||||
|
||||
在环境已经设置好了Mock,只需要在`/src/mock`中写代码即可。
|
||||
|
||||
### 多语言设置
|
||||
|
||||
多语言现在作为网络请求来发送,在pinia中设置本地持久存储。
|
||||
|
||||
后端需要按照返回格式进行传送
|
||||
|
||||

|
||||
|
||||
发送请求和初始化放在`App.vue`中
|
||||
|
||||

|
||||
|
||||
#### 多语言只做本地化
|
||||
|
||||
只需要在`i18n`中加入你的语言,之后在`index.ts`中引用即可
|
||||
|
||||

|
||||
|
||||
格式大致如下
|
||||
|
||||

|
||||
|
||||
#### 后端返回多语言格式
|
||||
|
||||
这个格式也可以改,默认的如下。需要包含默认语言`local:xxx`
|
||||
|
||||
```
|
||||
{
|
||||
"i18n": {
|
||||
"zh_cn": {
|
||||
"login": {
|
||||
"reset": "重置",
|
||||
"register": "登录"
|
||||
},
|
||||
"home": {
|
||||
"articleModeAlbum": "相册模式",
|
||||
"articleModeList": "列表模式"
|
||||
},
|
||||
"tabs": {},
|
||||
"header": {
|
||||
"fullScreen": "全屏",
|
||||
"exitFullScreen": "退出全屏",
|
||||
"personalData": "个人信息",
|
||||
"changePassword": "修改密码",
|
||||
"logout": "退出登录"
|
||||
},
|
||||
"tip": {
|
||||
"loading": "加载中..."
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"login": {
|
||||
"reset": "reset",
|
||||
"register": "register"
|
||||
},
|
||||
"home": {
|
||||
"articleModeAlbum": "Album Mode",
|
||||
"articleModeList": "List Mode"
|
||||
},
|
||||
"tabs": {},
|
||||
"header": {
|
||||
"fullScreen": "Full Screen",
|
||||
"exitFullScreen": "Exit Full Screen",
|
||||
"personalData": "Personal Data",
|
||||
"changePassword": "Change Password",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"tip": {
|
||||
"loading": "Loading..."
|
||||
}
|
||||
},
|
||||
"local": "zh_cn"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vue指令
|
||||
|
||||
配置了打印、复制、水印等指令
|
||||
|
||||

|
||||
```
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,54 @@
|
|||
<script setup lang="ts">
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
import { onMounted } from 'vue';
|
||||
import Konva from 'konva/lib';
|
||||
|
||||
const { width, height } = useWindowSize();
|
||||
|
||||
const initial = () => {
|
||||
const stage = new Konva.Stage({ container: 'container', width: width.value, height: height.value });
|
||||
const layer = new Konva.Layer({ draggable: true });
|
||||
|
||||
// 限制横向移动
|
||||
const horizontally = new Konva.Rect({
|
||||
x: 20,
|
||||
y: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'red',
|
||||
draggable: true,
|
||||
dragBoundFunc(pos) {
|
||||
return {
|
||||
x: pos.x,
|
||||
y: this.absolutePosition().y,
|
||||
};
|
||||
},
|
||||
});
|
||||
layer.add(horizontally);
|
||||
|
||||
const vertically = new Konva.Rect({
|
||||
x: 160,
|
||||
y: 20,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: 'green',
|
||||
draggable: true,
|
||||
dragBoundFunc(pos) {
|
||||
return { x: this.absolutePosition().x, y: pos.y };
|
||||
},
|
||||
});
|
||||
layer.add(vertically);
|
||||
|
||||
stage.add(layer);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initial();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="container"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,77 @@
|
|||
<script setup lang="ts">
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import Konva from 'konva/lib';
|
||||
import { Circle } from 'konva/lib/shapes/Circle';
|
||||
import { Text } from 'konva/lib/shapes/Text';
|
||||
import { Layer } from 'konva/lib/Layer';
|
||||
|
||||
const { width, height } = useWindowSize();
|
||||
const circle = ref<Circle>();
|
||||
const text = ref<Text>();
|
||||
const layer = ref<Layer>();
|
||||
|
||||
const initial = () => {
|
||||
const stage = new Konva.Stage({ container: 'container', width: width.value, height: height.value });
|
||||
layer.value = new Konva.Layer({ draggable: true });
|
||||
|
||||
text.value = new Konva.Text({
|
||||
x: 0,
|
||||
y: 0,
|
||||
fontSize: 20,
|
||||
fill: 'red',
|
||||
});
|
||||
layer.value.add(text.value);
|
||||
|
||||
circle.value = new Konva.Circle({
|
||||
x: stage.width() / 2,
|
||||
y: stage.height() / 2,
|
||||
radius: 70,
|
||||
fill: '#adf',
|
||||
stroke: 'black',
|
||||
strokeWidth: 4,
|
||||
listening: false,
|
||||
strokeScaleEnabled: false,
|
||||
scaleX: 2,
|
||||
});
|
||||
circle.value?.on('mouseover', function () {
|
||||
text.value?.text('鼠标经过');
|
||||
});
|
||||
circle.value?.on('mouseout', function () {
|
||||
text.value?.text('鼠标移出');
|
||||
});
|
||||
layer.value.add(circle.value);
|
||||
|
||||
stage.add(layer.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* * 开始监听
|
||||
*/
|
||||
const onStartListening = () => {
|
||||
circle.value?.listening(true);
|
||||
layer.value?.drawHit();
|
||||
};
|
||||
|
||||
/**
|
||||
* * 移出监听
|
||||
*/
|
||||
const onRemoveListening = () => {
|
||||
circle.value?.listening(false);
|
||||
layer.value?.drawHit();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initial();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container position-absolute start-0 top-6 z-1">
|
||||
<button type="button" class="btn btn-primary" @click="onStartListening">开始监听</button>
|
||||
<button type="button" class="btn btn-danger" @click="onRemoveListening">移出监听</button>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -40,11 +40,13 @@ const initial = () => {
|
|||
fill: 'green',
|
||||
stroke: 'black',
|
||||
strokeWidth: 4,
|
||||
duration: 1,
|
||||
});
|
||||
layer.add(circle.value);
|
||||
|
||||
// 添加事件
|
||||
circle.value?.on('click', function () {
|
||||
circle.value?.radius(80);
|
||||
text.value?.text(`点击了:${count.value}`);
|
||||
count.value++;
|
||||
});
|
||||
|
|