增加对于 mac 的支持 | 重构按键映射代码

This commit is contained in:
锦恢 2024-05-17 20:39:26 +08:00
parent 703ba8a58c
commit 55f0c5c93b
30 changed files with 769 additions and 208 deletions

View File

@ -1,24 +1,6 @@
# digital-wavetrace
## Project setup
```
npm install
```
## build
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
```bash
python scripts/vscode-package.py
```

13
package-lock.json generated
View File

@ -26,6 +26,7 @@
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint-plugin-vue": "^8.0.3",
"ignore-loader": "^0.1.2",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0"
}
@ -6991,6 +6992,12 @@
"node": ">= 4"
}
},
"node_modules/ignore-loader": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz",
"integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==",
"dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
@ -17333,6 +17340,12 @@
"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"dev": true
},
"ignore-loader": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz",
"integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==",
"dev": true
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",

View File

@ -26,6 +26,7 @@
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint-plugin-vue": "^8.0.3",
"ignore-loader": "^0.1.2",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0"
},

View File

@ -1,16 +1,43 @@
@font-face {
font-family: "iconfont"; /* Project id 4440655 */
src: url('iconfont.woff2?t=1713691301672') format('woff2');
src: url('iconfont.woff2?t=1715948037690') format('woff2');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-arrow-right:before {
content: "\eb08";
}
.icon-arrow-left:before {
content: "\eb09";
}
.icon-arrow-down:before {
content: "\eb0a";
}
.icon-arrow-up:before {
content: "\eb0b";
}
.icon-command:before {
content: "\e604";
}
.icon-minus:before {
content: "\e656";
}
.icon-equal:before {
content: "\e701";
}
.icon-ctrl:before {
content: "\e605";
}

Binary file not shown.

View File

@ -11,11 +11,12 @@
<link rel="stylesheet" href="vscode.css">
<link rel="stylesheet" href="vcd.css">
<link rel="stylesheet" href="iconfont.css">
<script src="vcd.js"></script>
<!-- <script src="vcd.js"></script> -->
<script></script>
<script>
window.readVcdFile = async () => {
const response = await fetch('pe_tb.vcd');
const response = await fetch('test.vcd');
const blob = await response.blob();
const reader = new FileReader();
return new Promise((resolve, reject) => {
@ -26,6 +27,8 @@
reader.readAsArrayBuffer(blob);
});
}
window.workerPath = 'worker.js';
window.workerRoot = '';
</script>
</head>

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1,9 +1,18 @@
importScripts('vcd.js');
self.onmessage = async event => {
const arrayBuffer = event.data;
const { arrayBuffer, workerRoot } = event.data;
self.locateFile = wasmBinaryFile => {
if (workerRoot) {
return workerRoot + (workerRoot.endsWith('/') ? '': '/') + wasmBinaryFile;
} else {
return wasmBinaryFile;
}
};
importScripts('vcd.js');
const vcdstream = await makeVcdStream();
vcdstream.consume(arrayBuffer);
const info = vcdstream.getBasicInfo();
self.postMessage(info);
}
};

14
scripts/vscode-package.py Normal file
View File

@ -0,0 +1,14 @@
import os
os.system('npm run build')
for file in os.listdir('dist'):
if file.endswith('.vcd'):
os.remove('dist/' + file)
with open('./dist/index.html', 'r', encoding='utf-8') as fp:
html = fp.read()
html = html.replace("''", "'<workerRoot>'")
with open('./dist/index.html', 'w', encoding='utf-8') as fp:
fp.write(html)

View File

@ -22,6 +22,7 @@ import { ElLoading } from 'element-plus';
import { emitter, globalLookup, globalSetting } from '@/hook/global';
import { makeWaveView } from '@/hook/render';
import { getCrossOriginWorkerURL } from '@/hook/network';
import Sidebar from '@/components/sidebar.vue';
import RightNav from '@/components/right-nav.vue';
@ -83,13 +84,13 @@ onMounted(async () => {
// vcd
const arrayBuffer = await window.readVcdFile();
const worker = new Worker('worker.js', {
const url = await getCrossOriginWorkerURL(window.workerPath);
const worker = new Worker(url, {
name: 'vcd-stream',
type: 'classic'
});
worker.postMessage(arrayBuffer, [arrayBuffer]);
worker.postMessage({ arrayBuffer, workerRoot: window.workerRoot }, [arrayBuffer]);
worker.addEventListener('message', event => {
const workerVars = event.data;

View File

@ -3,17 +3,42 @@
<div class="usermanual">
<h2>{{ t('usermanual') }}</h2>
<div class="usermanual-item">
<div v-html="t('usermanual.left-right-scroll.title')"></div>
<div><span class="iconfont icon-mouse"/><span class="iconfont icon-up-down"/></div>
<div>{{ t('usermanual.left-right-scroll.caption') }}</div>
</div>
<div class="usermanual-item">
<div v-html="t('usermanual.up-down-scroll.title')"></div>
<div><span class="iconfont icon-mouse"/><span class="iconfont icon-left-right"/></div>
<div>{{ t('usermanual.up-down-scroll.caption') }}</div>
</div>
<div class="usermanual-item">
<div v-html="t('usermanual.xscale.title')"></div>
<div><span class="iconfont icon-ctrl"/> + <span class="iconfont icon-mouse"/><span class="iconfont icon-up-down"/></div>
<div>{{ t('usermanual.xscale.caption') }}</div>
</div>
<div class="usermanual-item">
<div><span class="iconfont icon-shift"/> + <span class="iconfont icon-mouse"/><span class="iconfont icon-up-down"/></div>
<div>{{ t('usermanual.up-down-scroll.caption') }}</div>
</div>
<div class="usermanual-item" v-if="platform.startsWith('Mac')">
<div><span class="iconfont icon-command"/> + <span class="iconfont icon-mouse"/><span class="iconfont icon-up-down"/></div>
<div>{{ t('usermanual.xscale.caption') }}</div>
</div>
<div class="usermanual-item" v-else>
<div><span class="iconfont icon-ctrl"/> + <span class="iconfont icon-equal"/> / <span class="iconfont icon-minus"/></div>
<div>{{ t('usermanual.xscale.caption') }}</div>
</div>
<div class="usermanual-item">
<div><span class="iconfont icon-shift"/> + <span class="iconfont icon-arrow-up"/> / <span class="iconfont icon-arrow-down"/></div>
<div>{{ t('usermanual.left-right-scroll.caption') }}</div>
</div>
<div class="usermanual-item">
<div><span class="iconfont icon-shift"/> + <span class="iconfont icon-arrow-left"/> / <span class="iconfont icon-arrow-right"/></div>
<div>{{ t('usermanual.up-down-scroll.caption') }}</div>
</div>
</div>
<br>
@ -44,11 +69,14 @@ export default {
window.open(url, '_blank');
}
const platform = navigator.platform;
const { t } = useI18n();
return {
goto,
t
t,
platform
}
}
}

View File

@ -126,7 +126,6 @@ export default {
/* 沿着纵轴(垂直方向)对齐 */
}
.time-scale-value {}
.time-scale-line {
width: 1px;

View File

@ -52,7 +52,6 @@ export default {
emitter.emit('tree-view', signals);
// color change into selected
globalLookup.currentModule = module;
console.log(module);
}
const expandManage = reactive({

View File

@ -107,6 +107,7 @@ export default {
padding-left: 3px;
cursor: pointer;
align-items: center;
font-size: 0.9rem;
}

51
src/hook/network.js Normal file
View File

@ -0,0 +1,51 @@
const type = 'application/javascript';
/**
*
* @param {string} originalWorkerUrl
* @param {{
* useBlob?: boolean
* skipSameOrigin?: boolean
* }} options
* @returns {Promise<string>}
*/
async function getCrossOriginWorkerURL (originalWorkerUrl, options = {}) {
const skipSameOrigin = options.skipSameOrigin || true;
const useBlob = options.useBlob || true;
// 本来就是同源的,不需要额外进行处理,直接返回
if (!originalWorkerUrl.includes('://') || originalWorkerUrl.includes(window.location.origin)) {
return originalWorkerUrl;
}
// 获取 worker 的 js 代码
const res = await fetch(originalWorkerUrl);
const workerJsCode = await res.text();
// worker 所在的文件夹 url
const workerPath = new URL(originalWorkerUrl).href.split('/');
workerPath.pop();
// 给 importScripts 的每一个引入的js文件增加 workerPath 组成绝对路径
const importScriptsFix = `const _importScripts = importScripts;
const _fixImports = (url) => new URL(url, '${workerPath.join('/') + '/'}').href;
importScripts = (...urls) => _importScripts(...urls.map(_fixImports));
`;
// 以 MIME 协议组成 blob从而进行装载
let resultURL = `data:${type},` + encodeURIComponent(importScriptsFix + workerJsCode);
if (useBlob) {
// 在外面再套一层 importScripts这样第二次请求得到的 worker 就不是 blob worker 了
// 直接使用 createObjectURL 请求一个 worker 的源码是不行的,因为这样得到的 blob worker
// 会有原本 worker 的 feature 无法使用
resultURL = URL.createObjectURL(
new Blob([`importScripts("${resultURL}")`], { type })
);
}
return resultURL;
}
export {
getCrossOriginWorkerURL
}

View File

@ -1,8 +1,13 @@
import { globalLookup, globalSetting } from "./global";
import { domContainer, pluginRenderTimeGrid, pluginRenderValues, mountTree,
genOnWheel, genKeyHandler, keyEvent } from './wave-view';
eventHandler } from './wave-view';
import registerWheelEvent from "./wave-view/wheel-event";
import { registerTouchendEvent, registerTouchmoveEvent, registerTouchstartEvent } from "./wave-view/touch-event";
import { findCurrentSignalValue } from './utils';
import { registerKeyEvent } from "./wave-view/key-event";
let mainRenderEl = null;
const canvasMap = new Map();
@ -36,6 +41,8 @@ function pluginLocalStore(desc, pstate /* , els */) {
* @param {HTMLElement} parentElement
*/
function makeWaveView(parentElement) {
const platform = navigator.platform;
if (!parentElement) {
parentElement = getMainRenderEl();
}
@ -57,7 +64,29 @@ function makeWaveView(parentElement) {
console.log('updater');
};
container.elo.container.addEventListener('wheel', genOnWheel(parentElement, container.pstate, globalLookup, keyEvent));
// 注册基本的响应事件
container.elo.container.addEventListener('wheel',
registerWheelEvent(parentElement, container.pstate, globalLookup, eventHandler)
);
// 好像必须是 document
document.addEventListener('keydown',
registerKeyEvent(parentElement, container.pstate, globalLookup, eventHandler)
);
// TODO: 触控支持
// container.elo.container.addEventListener('touchstart',
// registerTouchstartEvent(parentElement, container.pstate, globalLookup, eventHandler)
// );
// container.elo.container.addEventListener('touchmove',
// registerTouchmoveEvent(parentElement, container.pstate, globalLookup, eventHandler)
// );
// container.elo.container.addEventListener('touchend',
// registerTouchendEvent(parentElement, container.pstate, globalLookup, eventHandler)
// );
}
@ -78,7 +107,7 @@ function toggleRender(signal) {
} else {
globalLookup.currentWires.add(signal);
const signalItem = globalLookup.chango[signal.link];
console.log(signalItem, signal.link);
// console.log(signalItem, signal.link);
const { wave, kind } = signalItem;
const time = globalLookup.currentTime;

View File

@ -183,7 +183,7 @@ const domContainer = (obj) => {
// TODO : 使用更新的属性替换 contentRect
// 未来版本 contentRect 可能会被丢弃
let { width, height } = entry.contentRect;
console.log(width, height);
// console.log(width, height);
// height = height || 888;
// console.log('resizeObserver', width, height);
resizeHandler(width, height);

View File

@ -16,10 +16,10 @@ const xScaleLevels = {
// TODO: 优化横向滚动体验
const xOffsetLevel = {
1: 500,
2: 20,
3: 5,
4: 1,
1: 0.035,
2: 0.1,
3: 0.2,
4: 0.4,
5: 0.5
};
@ -31,12 +31,12 @@ const yOffsetLevel = {
5: 320
};
const pluso = {
const scaleUp = {
desc: 'Zoom in time',
fn: pstate => xScaleUpdate(pstate, () => pstate.xScale * xScaleLevels[globalSetting.HorizontalScalingRatio])
};
const minuso = {
const scaleDown = {
desc: 'Zoom out time',
fn: pstate => xScaleUpdate(pstate, () => pstate.xScale / xScaleLevels[globalSetting.HorizontalScalingRatio])
};
@ -49,11 +49,19 @@ const fullo = {
const scroll = {
left: {
desc: 'Scroll into the past',
fn: pstate => xOffsetUpdate(pstate, () => pstate.xOffset + pstate.time / xOffsetLevel[globalSetting.HorizontalRollRatio])
fn: pstate => {
const offset = xOffsetLevel[globalSetting.HorizontalRollRatio] * pstate.width;
// console.log(`change from ${pstate.xOffset} to ${pstate.xOffset + pstate.time / xOffsetLevel[globalSetting.HorizontalRollRatio]}`);
return xOffsetUpdate(pstate, () => pstate.xOffset + offset);
}
},
right: {
desc: 'Scroll into the future',
fn: pstate => xOffsetUpdate(pstate, () => pstate.xOffset - pstate.time / xOffsetLevel[globalSetting.HorizontalRollRatio])
fn: pstate => {
// console.log(`change from ${pstate.xOffset} to ${pstate.xOffset - pstate.time / xOffsetLevel[globalSetting.HorizontalRollRatio]}`);
const offset = xOffsetLevel[globalSetting.HorizontalRollRatio] * pstate.width;
return xOffsetUpdate(pstate, () => pstate.xOffset - offset)
}
},
up: {
desc: 'scroll up',
@ -73,31 +81,16 @@ const editable = {
};
export default {
// Alt + <, >. left / right
// 'Alt+,': scroll.left,
// 'Alt+.': scroll.right,
// // Alt + [ ] home / end
// 'Alt+[': { desc: 'Jump to beginning of time', fn: pstate => xOffsetUpdate(pstate, pstate.sidebarWidth) }, // Home
// 'Alt+]': { desc: 'Jump to end time', fn: pstate => xOffsetUpdate(pstate, pstate.width - pstate.xScale * pstate.time) }, // End
// // ALT + - +
// 'Alt+=': pluso, // '+': pluso, '=': pluso,
// 'Alt+-': minuso, // '-': minuso, '_': minuso,
// 'Alt+0': fullo, // 'Shift+f': fullo, F: fullo, 'Shift+F': fullo,
// wheel
scrollLeft: scroll.left,
scrollRight: scroll.right,
scrollUp: scroll.up,
scrollDown: scroll.down,
scaleUp: scaleUp,
scaleDown: scaleDown,
'Shift+icon:scrollUp': scroll.left,
'Shift+icon:scrollDown': scroll.right,
'Ctrl+icon:scrollUp': pluso,
'Ctrl+icon:scrollDown': minuso,
// key
nop: { fn: () => false }
};

View File

@ -1,31 +0,0 @@
const { keyName } = require('w3c-keyname');
const executeKeyHandler = (key, keyEvent, pstate, cm) => {
return (keyEvent[key] || keyEvent.nop).fn(pstate, cm);
};
const genKeyHandler = (div, pstate, deso, cm, keyEvent, plugins) => {
return event => {
const modifier = (
(event.ctrlKey ? 'Ctrl+' : '') +
(event.shiftKey ? 'Shift+' : '') +
(event.altKey ? 'Alt+' : '')
);
// const key = modifier + event.key;
const key = modifier + keyName(event);
if (executeKeyHandler(key, keyEvent, pstate, cm)) {
event.stopPropagation();
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render();
}
};
};
export default {
executeKeyHandler,
genKeyHandler
};

View File

@ -1,45 +0,0 @@
const genOnWheel = (element, pstate, deso, keyEvent, plugins) =>
event => {
const { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
? 'Ctrl+icon:scrollUp'
: ((deltaY > 0) ? 'Ctrl+icon:scrollDown' : 'nop');
if (keyEvent[key].fn(pstate)) {
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (event.shiftKey) {
const key = (deltaY < 0) ? 'Shift+icon:scrollUp' : ((deltaY > 0) ? 'Shift+icon:scrollDown' : 'nop');
if (keyEvent[key].fn(pstate)) {
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX !== 0 && deltaY === 0) {
const key = (deltaX < 0) ? 'scrollLeft': 'scrollRight';
if (keyEvent[key].fn(pstate)) {
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX === 0 && deltaY !== 0) {
const key = (deltaY < 0) ? 'scrollUp': 'scrollDown';
if (keyEvent[key].fn(pstate)) {
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render({ type: 'action' });
}
event.preventDefault();
}
};
export default genOnWheel;

View File

@ -3,10 +3,9 @@ import getListing from './get-listing';
import domContainer from './dom-container';
import pluginRenderValues from './plugin-render-values';
import pluginRenderTimeGrid from './plugin-render-time-grid';
import keyEvent from './keyEvent';
import eventHandler from './event-handler';
import mountTree from './mount-tree';
import genKeyHandler from './gen-key-handler';
import genOnWheel from './gen-on-wheel';
import registerWheelEvent from './wheel-event';
import xOffsetUpdate from './x-offset-update';
import getX from './get-x';
import getT from './get-t';
@ -18,10 +17,9 @@ export {
domContainer,
pluginRenderValues,
pluginRenderTimeGrid,
keyEvent,
eventHandler,
mountTree,
genKeyHandler,
genOnWheel,
registerWheelEvent,
xOffsetUpdate,
getX,
getT

101
src/hook/wave-view/jsdoc.js Normal file
View File

@ -0,0 +1,101 @@
/**
* @description
* @typedef {Object} WaveElements
* @property {HTMLDivElement} grid
* @property {HTMLDivElement} view
* @property {HTMLDivElement} values
*/
/**
* @description
* @typedef {Object} ChangoItem
* @property {string} kind
* @property {Array<number | string>} wave
* @property {WebGLVertexArrayObject} lineVao
* @property {WebGLVertexArrayObject} maskVao
*/
/**
* @description 装载在 globalLookup 中的记录 vcd 数据的字典
* @typedef {Record<string, ChangoItem>} Chango
*/
/**
* @description
* @typedef {Object} WireItem
* @property {string} kind
* @property {string} link
* @property {string} name
* @property {number} size
* @property {WireItem} parent
* @property {string} type
*/
/**
* @description
* @typedef {Array<{ ref: string }>} View
*/
/**
* @description
* @typedef {Set<WireItem>} CurrentWires
*/
/**
* @description
* @typedef {Object} GlobalLookup
* @property {Chango} chango
* @property {View} view
* @property {CurrentWires} currentWires
* @property {number} time
*/
/**
* @description
* @typedef {Object} Pstate
* @property {number} width
* @property {number} height
* @property {number} xScale
* @property {number} xOffset
* @property {number} yOffset
* @property {number} yStep
* @property {number} yDuty
*/
/**
* @description
* @typedef {Object} Rgba
* @property {number} red
* @property {number} green
* @property {number} blue
* @property {number} alpha
*/
/**
* @description
* @typedef {Object} Updater
* @property {number} index
* @property {Rgba} rgba
*/
/**
* @description
* @typedef {Object} RenderConfig
* @property {'common' | 'action' | undefined} type
*/
/**
* @typedef {Object} Handler
* @property {string} desc
* @property {(arg1: Pstate, arg2: any) => boolean} fn
*/
/**
* @typedef {Object} Deso
* @property {(config: RenderConfig) => void} render
*
*/
/**
* @typedef {'scrollLeft' | 'scrollRight' | 'scrollUp' | 'scrollDown' | 'scaleUp' | 'scaleDown'} EventHandlerKind
*/

View File

@ -0,0 +1,163 @@
const platform = navigator.platform;
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<EventHandlerKind, Handler>} eventHandler
* @param {KeyboardEvent} event
*/
function windowsKeydown(element, pstate, deso, eventHandler, event) {
if (event.ctrlKey) {
if (event.key === '=') {
eventHandler['scaleUp'].fn(pstate);
deso.render();
} else if (event.key === '-') {
eventHandler['scaleDown'].fn(pstate);
deso.render();
}
}
if (event.shiftKey) {
switch (event.key) {
case 'ArrowLeft':
eventHandler['scrollLeft'].fn(pstate);
deso.render();
break;
case 'ArrowRight':
eventHandler['scrollRight'].fn(pstate);
deso.render();
break;
case 'ArrowUp':
eventHandler['scrollUp'].fn(pstate);
deso.render();
break;
case 'ArrowDown':
eventHandler['scrollDown'].fn(pstate);
deso.render();
break;
default:
break;
}
}
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<EventHandlerKind, Handler>} eventHandler
* @param {KeyboardEvent} event
*/
function linuxKeydown(element, pstate, deso, eventHandler, event) {
if (event.ctrlKey) {
if (event.key === '=') {
eventHandler['scaleUp'].fn(pstate);
deso.render();
} else if (event.key === '-') {
eventHandler['scaleDown'].fn(pstate);
deso.render();
}
}
if (event.shiftKey) {
switch (event.key) {
case 'ArrowLeft':
eventHandler['scrollLeft'].fn(pstate);
deso.render();
break;
case 'ArrowRight':
eventHandler['scrollRight'].fn(pstate);
deso.render();
break;
case 'ArrowUp':
eventHandler['scrollUp'].fn(pstate);
deso.render();
break;
case 'ArrowDown':
eventHandler['scrollDown'].fn(pstate);
deso.render();
break;
default:
break;
}
}
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<EventHandlerKind, Handler>} eventHandler
* @param {KeyboardEvent} event
*/
function macOsKeydown(element, pstate, deso, eventHandler, event) {
if (event.metaKey) {
if (event.key === '=') {
eventHandler['scaleUp'].fn(pstate);
deso.render();
} else if (event.key === '-') {
eventHandler['scaleDown'].fn(pstate);
deso.render();
}
}
if (event.shiftKey) {
switch (event.key) {
case 'ArrowLeft':
eventHandler['scrollLeft'].fn(pstate);
deso.render();
break;
case 'ArrowRight':
eventHandler['scrollRight'].fn(pstate);
deso.render();
break;
case 'ArrowUp':
eventHandler['scrollUp'].fn(pstate);
deso.render();
break;
case 'ArrowDown':
eventHandler['scrollDown'].fn(pstate);
deso.render();
break;
default:
break;
}
}
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @returns {(event: KeyboardEvent) => void}
*/
function registerKeyEvent(element, pstate, deso, eventHandler) {
const platform = navigator.platform;
if (platform.startsWith('Win')) {
return event => windowsKeydown(element, pstate, deso, eventHandler, event);
} else if (platform.startsWith('Linux')) {
return event => linuxKeydown(element, pstate, deso, eventHandler, event);
} else if (platform.startsWith('Mac')) {
return event => macOsKeydown(element, pstate, deso, eventHandler, event);
} else {
throw Error('不支持的操作系统!');
}
}
export {
registerKeyEvent
};

View File

@ -3,46 +3,15 @@ import { globalSetting, globalStyle } from '../global';
import { gl_Colors, gl_Shifts, gl_Shifts_for_bar, gl_Shifts_map, gl_WidthShifts, barShift, getRatio, screenHeightPixel } from './render-utils.js';
import { vertexShader, fragmentShader } from './render-shader.js';
// const { ChangoItem } = require('./types.d.ts');
class WebGL2WaveRender {
/**
*
* @param {{
* grid: HTMLDivElement,
* view: HTMLDivElement,
* values: HTMLDivElement
* }} elements
* @param {{
* chango: Record<string, {
* kind: string,
* wave: Array<number | string>,
* lineVao: WebGLVertexArrayObject
* maskVao: WebGLVertexArrayObject
* }>
* view: Array<{ ref: string }>,
* currentWires: Set<{
* kind: string,
* link: string,
* name: string,
* size: number,
* parent: object,
* type: string
* }>,
* time: number
* }} globalLookup
* @param {{
* width: number,
* height: number,
* xScale: number,
* xOffset: number,
* yOffset: number,
* yStep: number,
* yDuty: number
* }} pstate
* @param { Array } plugins
* @param {WaveElements} elements
* @param {GlobalLookup} globalLookup
* @param {Pstate} pstate
* @param {Array} plugins
*/
constructor(elements, globalLookup, pstate, plugins) {
const canvas = document.createElement('canvas');
@ -144,9 +113,6 @@ class WebGL2WaveRender {
} else if (kind === 'vec') {
const { lineVertices, maskVertices } = this.makeVecVertex(wave, time);
return { lineVertices, maskVertices };
} else if (kind === '') {
const { lineVertices, maskVertices } = this.makeVecVertex(wave, time);
return { lineVertices, maskVertices };
}
return {
lineVertices: undefined,
@ -277,7 +243,7 @@ class WebGL2WaveRender {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
} else {
const lastPoint = perspectivePoints.at(-1);
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if ((lastPoint.y !== renderParam1.y) || (lastPoint.color !== renderParam1.color)) {
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
}
@ -288,7 +254,7 @@ class WebGL2WaveRender {
}
// 确保最后一个点延申到了 time
const lastPoint = perspectivePoints.at(-1);
const lastPoint = perspectivePoints[perspectivePoints.length - 1];
if (lastPoint.x < time) {
perspectivePoints.push({ x: time, y: lastPoint.y, color: lastPoint.color });
}
@ -520,15 +486,7 @@ class WebGL2WaveRender {
/**
*
* @param {Array<{
* index: number
* rgba: {
* red: number,
* green: number,
* blue: number,
* alpha: number
* }
* }>} updaters
* @param {Array<Updater>} updaters
* @param {{
* updateMask: boolean
* }} config
@ -559,9 +517,7 @@ class WebGL2WaveRender {
/**
*
* @param {{
* type: 'common' | 'action'
* } | undefined} renderConfig
* @param {RenderConfig} renderConfig
*/
render(renderConfig) {
renderConfig = renderConfig || { type: 'common' };

View File

@ -0,0 +1,127 @@
// 触控事件
const platform = navigator.platform;
const touchState = {
startX: undefined,
startY: undefined
};
// TODO: 完成这个 和 手势控制
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {TouchEvent} event
*/
function macTouchmove(element, pstate, deso, eventHandler, event) {
const touch = event.touches[0];
if (touch) {
const { pageX, pageY } = touch;
const deltaX = pageX - touchState.startX;
const deltaY = pageY - touchState.startY;
const updownKey = (deltaY < 0) ? 'scrollDown' : 'scrollUp';
const leftrightKey = (deltaX < 0) ? 'scrollRight' : 'scrollLeft';
eventHandler[updownKey].fn(pstate);
deso.render();
// eventHandler[leftrightKey].fn(pstate);
// deso.render();
}
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {TouchEvent} event
*/
function macTouchstart(element, pstate, deso, eventHandler, event) {
const touch = event.touches[0];
if (touch) {
const { pageX, pageY } = touch;
touchState.startX = pageX;
touchState.startY = pageY;
}
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {TouchEvent} event
*/
function macTouchend(element, pstate, deso, eventHandler, event) {
touchState.startX = undefined;
touchState.startY = undefined;
event.preventDefault();
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @returns {(event: TouchEvent) => void}
*/
function registerTouchmoveEvent(element, pstate, deso, eventHandler) {
if (platform.startsWith('Mac')) {
// IOS
return event => macTouchmove(element, pstate, deso, eventHandler, event);
} else {
return event => {}
}
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @returns {(event: TouchEvent) => void}
*/
function registerTouchstartEvent(element, pstate, deso, eventHandler) {
if (platform.startsWith('Mac')) {
return event => macTouchstart(element, pstate, deso, eventHandler, event);
} else {
return event => {}
}
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @returns {(event: TouchEvent) => void}
*/
function registerTouchendEvent(element, pstate, deso, eventHandler) {
if (platform.startsWith('Mac')) {
return event => macTouchend(element, pstate, deso, eventHandler, event);
} else {
return event => {}
}
}
export {
registerTouchstartEvent,
registerTouchmoveEvent,
registerTouchendEvent
};

View File

@ -0,0 +1,136 @@
// 鼠标滚轮事件
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {WheelEvent} event
*/
function windowsWheel(element, pstate, deso, eventHandler, event) {
const { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
? 'scaleUp'
: ((deltaY > 0) ? 'scaleDown' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (event.shiftKey) {
const key = (deltaY < 0) ? 'scrollLeft' : ((deltaY > 0) ? 'scrollRight' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX !== 0 && deltaY === 0) {
const key = (deltaX < 0) ? 'scrollLeft' : 'scrollRight';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX === 0 && deltaY !== 0) {
const key = (deltaY < 0) ? 'scrollUp' : 'scrollDown';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
}
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {WheelEvent} event
*/
function macOsWheel(element, pstate, deso, eventHandler, event) {
let { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
? 'scaleUp'
: ((deltaY > 0) ? 'scaleDown' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (event.shiftKey) {
const key = (deltaX < 0) ? 'scrollLeft' : ((deltaX > 0) ? 'scrollRight' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
}else if (deltaX !== 0 && deltaY === 0) {
const key = (deltaX < 0) ? 'scrollLeft' : 'scrollRight';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX === 0 && deltaY !== 0) {
const key = (deltaY < 0) ? 'scrollUp' : 'scrollDown';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
}
}
/**
*
* @param {WaveElements} element
* @param {Pstate} pstate
* @param {Deso} deso
* @param {Record<string, Handler>} eventHandler
* @param {WheelEvent} event
*/
function linuxWheel(element, pstate, deso, eventHandler, event) {
const { deltaX, deltaY } = event;
if (event.ctrlKey) {
const key = (deltaY < 0)
? 'scaleUp'
: ((deltaY > 0) ? 'scaleDown' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (event.shiftKey) {
const key = (deltaY < 0) ? 'scrollLeft' : ((deltaY > 0) ? 'scrollRight' : 'nop');
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX !== 0 && deltaY === 0) {
const key = (deltaX < 0) ? 'scrollLeft' : 'scrollRight';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
} else if (deltaX === 0 && deltaY !== 0) {
const key = (deltaY < 0) ? 'scrollUp' : 'scrollDown';
if (eventHandler[key].fn(pstate)) {
deso.render({ type: 'action' });
}
event.preventDefault();
}
}
function registerWheelEvent(element, pstate, deso, eventHandler) {
const platform = navigator.platform;
if (platform.startsWith('Win')) {
return event => windowsWheel(element, pstate, deso, eventHandler, event);
} else if (platform.startsWith('Linux')) {
return event => linuxWheel(element, pstate, deso, eventHandler, event);
} else if (platform.startsWith('Mac')) {
return event => macOsWheel(element, pstate, deso, eventHandler, event);
} else {
throw Error('不支持的操作系统!');
}
}
export default registerWheelEvent;

View File

@ -6,7 +6,7 @@ const yOffsetUpdate = (pstate, nextOffsetYFn) => {
const currentRenderHeight = globalLookup.currentWires.size * pstate.yStep;
const canvasHeight = pstate.height - pstate.topBarHeight - pstate.botBarHeight;
console.log(currentRenderHeight, canvasHeight);
// console.log(currentRenderHeight, canvasHeight);
const maxOffsetX = Math.max(-40, currentRenderHeight - canvasHeight); // maximum offset
nextOffsetY = Math.min(nextOffsetY, maxOffsetX);

View File

@ -46,7 +46,8 @@
"usermanual": "User Manual",
"usermanual.left-right-scroll.title": "<span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-up-down\"/>",
"usermanual.left-right-scroll.caption": "move up and down",
"usermanual.up-down-scroll.title": "<span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-left-right\"/> / <span class=\"iconfont icon-shift\"/> + <span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-up-down\"/>",
"usermanual.up-down-scroll.title.wheel": "<span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-left-right\"/> / <span class=\"iconfont icon-shift\"/> + <span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-up-down\"/>",
"usermanual.up-down-scroll.title.shift": "<span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-left-right\"/> / <span class=\"iconfont icon-shift\"/> + <span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-up-down\"/>",
"usermanual.up-down-scroll.caption": "move left and right",
"usermanual.xscale.title": "<span class=\"iconfont icon-ctrl\"/> + <span class=\"iconfont icon-mouse\"/> + <span class=\"iconfont icon-up-down\"/>",
"usermanual.xscale.caption": "scale along x axis",

View File

@ -12,4 +12,9 @@ module.exports = {
}),
],
devtool: 'none',
}
module: {
loader: [
{ test: /\.vcd$/, loader: 'ignore-loader' }
]
}
};