win 掌机远程控制 ps5

其实一共有两套方案 稍微麻烦一点的时使用 chiaki 或者和我一样使用 psremoteplay chiaki chiaki 也叫千秋其实已经是一个非常成熟的工具了 https://sr.ht/~thestr4ng3r/chiaki/ 主要麻烦的一点是,你需要自己获取对应的 psn accound id Obtaining your PSN AccountID Starting with PS4 7.0, it is necessary to use a so-called “AccountID” as opposed to the “Online-ID” for registration (streaming itself did not change). This ID seems to be a unique identifier for a PSN Account and it can be obtained from the PSN after logging in using OAuth. A Python 3 script which does this is provided in scripts/psn-account-id.py. Simply run it in a terminal and follow the instructions. Once you know your ID, write it down. You will likely never have to do this process again. ...

May 5, 2024 · 1 min · 137 words · 水华

纯css让element-ui表格实现吸顶效果

Position Sticky 与表格头 原文章https://css-tricks.com/position-sticky-and-table-headers/. 本身产品有个需求需要表格自动吸顶,本身用了 antd design 的 scroll 实现的,现在使用 css 实现,效果更好 thead和tr上无法设置 styleposition: sticky;, 到那时在th上却可以,这也意味着,其实你可以在一个传统的table上实现吸顶 header 效果,如果你不清楚吸顶实现的原理,大概实现起来会很棘手吧,使用 css 总比原先用 js 去监听事件然后改变position好得多 兼容性 查看兼容性 只要不是 ie 这种活化石,基本都已经支持了 使用 保证 table 的 position 为 relative 给每个 th 加上 sticky 保证其父容器没有overflow: hidden 例子 .sticky-head >>>.el-table overflow visible .el-table__header-wrapper overflow visible position sticky top 0 z-index 10 table position sticky top 0 thead position relative th position sticky top 0 z-index 10

May 2, 2024 · 1 min · 69 words · 水华

axios兜底错误处理

项目的网络请求层使用了axios,axios是一个及其好用的库,但是在业务使用上出现了一些问题 出现原因 众所周知,前端一般通过ajax向后端请求数据然后处理后显示在网页/app上,当然人都是懒得,总是希望有一些自动完成的事,比如这一次,如果不是需要特殊处理的特定错误,直接弹toast结束。 错误处理还是很容易,axios增加拦截器,如果是指定的错误,直接throw new Error('错误') 剩下的交给promise.catch干就好了,问题集中在如何兜底这个catch,如果不处理,会变成unhandledrejection错误 解决方案 最原始的方案 高阶函数 throwMessage,自动在promise之后增加catch函数,问题是这个要记得手动引入,和自动这个理念还是有点区别的. export function throwMessage<T = any>(responsePromise: Promise<T>) { return responsePromise.catch((e: Error) => { console.error(`error`, e) message.error(e.message) }) } 每次使用都要throwMessage(promise)容易忘,如果有必要,还要写lint规则防止忘记 webpack方案 既然打包的是webpack,那么使用webpack在转译原始代码的时候注入就好了嘛. 推荐一个promise-catch-loader自动处理好这一系列问题 具体因为没有太研究,不做展开 事件监听方案 上文提到了,如果promise没有捕获错误,会自动变成unhandlered rejection错误,这个是可以全局监听的,具体在MDN上可以看 那么 我们在window上直接监听就好了 window.addEventListener('unhandledrejection', event => { event.preventDefault() }); 问题是,太全局了 处理Promise链方案 这种是最终处理的方案,当然也是最魔改的方案,考虑到只运行在前端,内存溢出情况不多,方能实现 axios返回的是promise,promise是遵循PromiseA+方案,内部其实是有一条promise链的,我们的目的就在于,在promise链条的最后一个promise上挂上catch,反正预先被处理了最后一个也不会触发 问题来了 如果你直接看promise的话,是只能看到,then,catch,finally这些方法的,并没办法看到promise链,也没办法顺着链访问到最后 如何解决 我们的目标是将其变为链式结构,简单来说,只要每个then和catch都能记下他的后继即可。 所以最终我选择注入promise的两个方法 实现代码 /** * 继承然后注入两个方法 */ export class ChainedPromise<T> extends Promise<T> { nextPromise?: Promise<any> then<TResult1 = T, TResult2 = never>( onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null, ): Promise<TResult1 | TResult2> { const promise = super.then(onfulfilled, onrejected) this.nextPromise = promise return promise } catch<TResult = never>( onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null, ): Promise<T | TResult> { const promise = super.catch(onrejected) this.nextPromise = promise return promise } } /** * 暴露给外面的方法,axios通过拦截器,通过封装service都能调用它来在入口处自动注入 */ function autoTravel<T>(promise: Promise<T>): Promise<T> { const getLastPromise = function(promise: ChainedPromise<any>): Promise<any> { if (promise.nextPromise) { return getLastPromise(promise.nextPromise) } return promise } /** * Promise内部的自动形成链条,就能顺着链条找到最后一个了 */ const autoTravelPromise = new ChainedPromise<T>((resolve, reject) => { promise.then(resolve).catch(responseError => { // throwMessage为之前定义的,promise上自动加catch的方法 throwMessage(getLastPromise(autoTravelPromise)) reject(responseError) }) }) return autoTravelPromise }

April 29, 2024 · 1 min · 189 words · 水华

vue-cli项目如何同时打包一个express项目

vue-cli项目一般只会生成一个纯前端的vue项目,不过有时候,如果我们想开发一个express项目同时又想用vue作为前端该怎么搞呢? 几个解决方案 两个项目分别开发 mono-repo 其他方案 vue/express一个项目一次打包 其实因为怕打包麻烦,所以我是希望一个项目内,可以同时开发express/vue打包的时候又能只打包输出到一个文件夹下 所以我选择在vue-cli生产的项目内,增加一个server文件夹,用来开发express相关的代码,只有在最后打包的时候,变为一个express项目 改造的要点 前后端独立的hmr 前后端最终成果打包在一起 改造过程 独立热更同时前端可调用express服务 首先,我们在文件目录下建立server文件夹,将express服务器的开发,src下为原先前端相关的代码 其次,安装nodemon用来做express项目的开发启动 package.json添加命令"start:be": "nodemon ./server/index.js" 添加vue.config.js, 加入 module.exports = { devServer: { proxy: { '/{express服务的路径,例如api}': { target: 'http://127.0.0.1:{express端口}, ws: true, }, } } } 这样,启动npm run serve的时候同时启动start: be即可同时开启前后端 打包让express应用调用vue结果 express有static功能,假定用的是public 文件夹 package.json添加命令"build:be": "cp package.json ./dist/ && cp server/* ./dist/" 这样来打包express应用 同时express内添加一行,app.use(express.static('./public')) 接着,来改造vue.config.js module.exports = { outputDir: "./dist/public" } 之后build命令运行完,运行build:fe即可 ...

June 18, 2022 · 1 min · 68 words · 水华

如何实现表格的列拖拽的

因为使用el-table实现,所以一些抓取dom的class类通过el-table内置的类实现,实际如果是简单表格的话,可以自行增加class来实现 底层框架/原理 sortablejs 核心的拖拽原理,我们通过使用sortablejs提供的dom拖拽方案,实现 我们通过让sortablejs的el参数指定到el-table的header上 const query = ".el-table__header-wrapper thead tr" const el docuemnt.querySelector(query) // this.$el.querySelector(query) 那么表头的那一行的所有th就变为拖拽目标了,之后根据index的顺序变化,可以反推到列的切换上 核心代码 const sortable = new Sortable(el, { onEnd(evt) { let { newIndex, oldIndex, item } = evt; // 通知上级交换column位置 } }) 其他一些实现 跨表格实现 跨表格实现思路在于,通过在window上建立一个桥接用的map 缓存table的dom => vue实例对应关系 const sortable = new Sortable(el, { onEnd(evt) { const { to, from, pullMode } = evt; const toContext = window.bridge.get(to) const fromContext = window.bridge.get(from) let { newIndex, oldIndex, item } = evt; // 通知from和to对应的数据进行切换即可 } }) 拖拽优化 虽然核心代码很简单,但是不够完美,拖拽的时候只有表头可以进行拖动,实际上整列是没有跟着一起拖动的 所以我们需要进行样式上的优化,主要有两点 拖拽时候的影子 该列所有td跟随表头拖动 ...

December 11, 2021 · 5 min · 875 words · 水华

el-table拖拽开发实践

当前使用的 element-ui-el-table-draggable 提供了对element-ui内el-table的行进行拖拽排序的能力 不足之处 element-ui-el-table-draggable 只能配置两个参数,不支持列拖拽,不支持类似group等参数 改进和开发记录 基本属于重写了, 根据核心原理做了一个出来, 也就是,dom结构使用.el-table__body-wrapper tbody,然后直接交换el-table这个data对应index的数据 重点提示,需要给el-table增加row-key,保证交换之后重新渲染的数据正确!!! const elTableContext = this.$children[0] // 因为是通过slot引入 const container = elTableContext.$el.querySelector('.el-table__body-wrapper tbody') Sortable.create(container, { onEnd(evt) { let { newIndex, oldIndex, } = evt // 交换elTableContext.data里的位置,不展开了 exchange(oldIndex, newIndex) this.$emit('sort') } }) 之后我们解决几个核心问题 不能使用sortable.js的配置(例如group属性来多列表之间拖拽) 跨表格数据更新 支持列拖拽 expanded的row特殊处理 空处理 sortable.js配置 这个好解决,一方面是可以配置props, 另一方面,我们可以使用$attrs这个属性,将未在props内定义的属性直接获取 Sortable.create(container, { ...this.$attrs, // sortable的onXXX事件转为vue的事件格式emit掉 ...Object.keys(this.$listeners).reduce((events, key) => { const handler = this.$listeners[key] // 首字母大写 const eventName = `on${key.replace(/\b(\w)(\w*)/g, function($0, $1, $2) { return $1.toUpperCase() + $2.toLowerCase() })}` events[eventName] = (...args) => handler(...args) return events }, {}), onEnd(evt) { // 之前的处理代码 this.$emit('end', evt) }, }) 同时增加一个监听,自动更新对应的参数 ...

August 2, 2021 · 2 min · 295 words · 水华

借助Blazor在浏览器运行dotnet代码

借助Blazor在浏览器运行dotnet代码 本文重点,想看步骤的请直接移步正文 如何精简Blazor的代码,移除界面部分 如何和c#部分交互 如何访问FS部分 打包构建 因为工作上的需要,需要在网页上进行usm格式的视频,但是usm的视频格式并没有相关的说明,导致也没办法和之前一样直接通过读取二进制流的方式解码然后转mp4流喂给<video> 不过业内也都是使用CriDemuxer来对usm视频格式进行解交织的,而且CriDemuxer有开源,只不过代码是基于C#的,所以最后问题变成了,怎么在网页中运行C#代码 碎碎念结束 BLAZOR 目前将c#运行在浏览器端主要靠编译成wasm运行,在net5之前,想让dotnet多端运行就得借助mono进行编译,然后mono有推出过一个mono-wasm0.1的包,可以将编译为wasm可以因为维护的原因,已经下不到了 不过好在微软官方有一个借助mono-wasm的项目 Blazor 来实现用 C# 来构建网页,虽然用不上他的页面功能,但是四舍五入一下,却可以用它来实现在浏览器端运行 C# 代码 实现步骤 安装环境 根据官方教程即可 https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor 创建项目 我们创建一个基于wasm的app dotnet new blazorwasm -o BlazorApp --no-https 然后项目就创建完了,记住和官方教程不一样,是blazorwasm dev cd BlazorApp dotnet watch run 一个基础项目就起来了,之后开始进行改造 移除页面相关文件 目录 pages Shared 文件 App.razor _Imports.razor 修改 Program.cs移除` builder.RootComponents.Add("#app"); `这行 这个时候运行,页面上应该就只有一个Loading...了,如果觉得难受,可以修改wwwroot下的index.html文件 编写代码 之后整个项目其实就是一个标准的dotnet项目了,可以直接将编写的代码移动进来,比如我这里就直接把CriDemuxerCore的代码直接移动进来 编写调用代码 在Program.cs内注入相关的js入口即可 这样的话,在浏览器内就能调用c#的代码了 using Microsoft.JSInterop; [JSInvokable] public static async Task<string> Demux(string filePath) { if (File.Exists(filePath)) { MpegStream.DemuxOptionsStruct demuxOptions = new MpegStream.DemuxOptionsStruct(); demuxOptions.ExtractVideo = true; return await Task.Run(() => { Console.WriteLine("Start"); CriUsmStream criUsmStream = new CriUsmStream(filePath); return criUsmStream.DemultiplexStreams(demuxOptions)[0]; }); } else { throw new Exception(filePath + " Not exists"); } } 在浏览器中的调用方法 ...

May 10, 2021 · 1 min · 134 words · 水华

Electron构建如何支持m1芯片

起因 换了台m1的macbook, 我之前的看板electron程序构建的还是老的x64版本,使用上还需要使用rosetta2进行转译,性能损失不说,说不定还有潜在的问题,所幸看到 electron11开始支持使用arm64芯片的mac了,正好升级一波 写在前面 目前工具库还在preview,taobao镜像不可用 顺带提一下我之前的项目结构 基于 @vue/cli4 使用 vue-cli-plugin-electron-builder注入的electron相关 构建流 本地正常构建,自动构建流基于samuelmeuli/action-electron-builder@v1自动构建 项目地址点击这里 开工 升级依赖 首先需要升级使用的electron到支持新芯片的版本,目前能够使用的是electron11 npm install --save electron@11 同时我们需要升级electron-builder到支持打包新芯片的版本, 根据最新的方案,arch需要支持arm64并且可以打包dmg的话,需要升级到22.10.4 npm install --save-dev electron-builder@22.10.4 不过因为还在preview 只有npm官方仓库可以安装 接下来我们改造构建选项,因为我使用vue的插件, 所以我写在vue.config.js module.exports = { pluginOptions: { electronBuilder: { builderOptions: { artifactName: '${productName}-${version}-${os}-${arch}.${ext}', mac: { target: { arch: 'universal', target: 'dmg' } } }, } } } 这样可以打包一个自适应芯片的版本,当然你想打两个包可以把arch换成arch: ['x64', 'arm64'] ...

January 9, 2021 · 1 min · 78 words · 水华

用JS去读取【MMD】中vmd文件的那些事

未来能用js编写的软件,都会用js编写 前言 MikuMikuDance,简称MMD,如果是b站用户的话,应该都看到过一个专门的mmd分区,里面是各类舞蹈视频,这个是早期(真的很久了)给v家角色制作舞蹈动画的一个软件,当然除了v家6人以外,崩坏3,元神各类角色相关的宅舞投稿都有 mmd其实本质上还是一款三维动画制作软件,包含 模型 动作 关键帧等一系列相关的概念,模型的话,TGA式,大妈式,很多都有相关的配布,模型和镜头也有相关的发布,其实相关产业已经很完善了,但是这一次,我打算对mmd中保存动作,镜头,表情和关键帧的VMD(Vocaload Mation Data)文件下手了,当然主要还是因为。。。 mmd不支持mac!我不想装windows啊,作为前端工程师的我,如果想实现类似的渲染,那么,一方面是需要搞定模型的渲染**(Tree.js永远的神)**,另一方面就是能够编辑解析其中的vmd文件了 当然,为了解决动作的问题, 一方面,mmd提供了kinect进行捕捉,另一方面,我也想试试用posenet来解析动作,不过在这之前,先得吧vmd搞定 顺便,练习一下用js读取二进制流嘛 几个概念 二进制流 文件其实就是一堆 0,1 组成的二进制码流,在javascript的世界中,这种文件叫做ArrayBuffer, 虽然我们不能直接操作,但是做相应的读取还是没用问题的 你不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。 用js获取二进制流有几种办法,最常用的就是从file直接转过来,这里我们通过FileReader即可 FileReader 接口提供的 readAsArrayBuffer() 方法用于启动读取指定的 Blob 或 File 内容。当读取操作完成时,readyState 变成 DONE(已完成),并触发 loadend 事件,同时 result 属性中将包含一个 ArrayBuffer 对象以表示所读取文件的数据。 当然啦,还有很多种办法,比如把请求头设置成arrayBuffer等等,这里请查阅相关的文档吧 TypedArray 在真实的使用场景(比如其他语言体系,例如c)其实对bytes有很多种不同的编码方案,用来表示不同的类型,int型一般2个字节这种,在js中也有相对应的TypedArrays 只要参考对照表,其实读取方面就比较简单了 VMD文件的格式 光有文件流,还需要一套相应的读取方案才行,感谢国内两位大佬的相关文章,解惑了 ...

December 6, 2020 · 2 min · 235 words · 水华

如何发布一个vue组件到npm

如今做vue的相关开发,基本都离不开vue-cli,有时候,我们也会将自己工作中抽象出来的优秀方法或者组件发布给外网进行使用,本文基于自身写的seed组件系统整理一下如何自动化发布到npm的一个流程 一些前期准备 github账号和仓库地址 npmjs的账号 整体构建流程 既然要做,就考虑一下完全自动化构建发布吧,基于github actions来完成整个CI/CD工作,主要包含,代码的自动构建,自动发布文档到github.io以及自动发布到npm上 所以我们需要 vue-cli 创建项目 github code 用于在github action的时候访问授权 npmjs code 发布的时候用的code vuepress 可以在md中使用vue组件的文档构建工具 一些合理的命名,在组件发布到npm后,调用者也能正确的使用 开始安装vue-cli 请老手直接跳过 npm install -g @vue/cli # OR yarn global add @vue/cli vue create 你的项目名称 更改你的目录结构 初始化的vue项目目录基本是为了开发一个网页app使用的,然而我们的目标是构建一个lib所以需要将目录结构进行一定的更改 原本的src文件夹存放的是页面的源代码,在组件库构建中,相当于是一个演示目录,所以我们将其更名为examples, 同时在根目录下新建一个packages存放我们开发的lib需要的代码,当然,如果有一些通用的lib文件的话,也新建一个lib文件夹好了 既然目录结构改了,一些默认的入口地址也进行更换 增加jsconfig.json 这个主要为了方便vscode提示,大概例子为 { "compilerOptions": { "target": "esnext", "experimentalDecorators": true, "baseUrl": ".", "checkJs": false, "paths": { "@/*": [ "./examples/*" ], "{你的项目名称}/packages/*": [ "./packages/*" ], "{你的项目名称}/lib/*": [ "./lib/*" ], "types/*": [ "types/*" ] } }, "exclude": [ "node_modules", "lib", "docs", ".github", "public" ] } 增加vue.config.js 这里为了做别名引用,以及修改开发时的页面的入口文件地址为新的目标文件夹 ...

August 8, 2020 · 4 min · 726 words · 水华