当前位置: 首页 > Web前端 > vue.js

Vue3开发可自由配置的浏览器导航起始页

时间:2023-03-31 23:22:59 vue.js

前言Howdz是基于Vue3+Typescript开发的完全自定义的浏览器导航起始页。支持按需添加素材组件,可自由编辑组件的位置、大小和功能。支持响应式设计,可以自定义随机壁纸、动态壁纸等。项目提供在线访问网页、打包浏览器插件、打包桌面应用(Electron)等多种访问方式。本文记录了项目开发中使用的相关技术。表单封装工程中可以自由添加各种素材组件,每个素材组件都有自己的配置项表单,有的配置项相同,可以实现JS数据驱动的表单封装。目前使用的是ElementPlus框架,封装了一个StandardForm组件,将formData和formConf属性传递给它,生成双向绑定表单,支持JSX插入其他自定义组件。由于篇幅问题,组件封装代码可以参考这里:standard-form.vue然后就可以使用类似json的格式来实现各个material组件的配置形式。例如Weather组件的setting.tsx如下://@/materials/Weather/setting.tsximportpickfrom'../base'//pick可以自由选择publicconfigurationexportdefault{formData:{weatherMode:1,cityName:'',animationIcon:true,duration:15,position:5,baseFontSize:16,textColor:'#262626',textShadow:'001px#464646',iconShadow:'001px#464646',fontFamily:'',padding:10},formConf(formData:Record){//传入formData实现双向绑定return{weatherMode:{label:'weathercity',type:'radio-group',radio:{list:[{name:'自动获取(IP)',value:1},{name:'手动输入',value:2}],label:'name',value:'value'}},cityName:{when:(formData:Record)=>formData.weatherMode===2,//类似v-iftype:'input',attrs:{placeholder:'请输入城市名(目前只支持中文城市名)',clearable:true},规则:[{required:true,validator:(rule:unknown,value:string,callback:(e?:Error)=>void)=>{formData.weatherMode===2&&!value?callback(newError('Pleaseenterthecityname')):callback()}}]//支持el-form原生规则},animationIcon:{label:'动画图标',type:'switch',tips:'默认使用带动画的ICON,如果想提高性能,可以关闭使用staticICON'},duration:{label:'autorefreshfrequency',type:'input-number',attrs:{'controls-position':'right',min:5,max:12*60},tips:'刷新频率,单位是分钟'},...pick(formData,[//选择常用配置'position','baseFontSize','textColor','textShadow','iconShadow','fontFamily','padding'])}}}右键菜单材质组件添加后,可以在右键弹出菜单中编辑模式更改配置或删除等。右键菜单来源为作者开源@howdjs/mouse-menu。同时,在本项目中,为了兼容移动端,对插件进行了二次封装,并为其添加了长按弹出菜单的功能。二次包装码参考这里。项目使用vue命令方式。菜单插件可以接收任意参数进行回调,因此可以将点击的材质组件数据传递给回调进行各种操作。material组件布局目前提供2种布局方式,一种是基于类文件流的网格布局,允许组件一个接一个地排列,另一种是Fixed布局,允许组件固定在页面的任意位置。Grid模式Grid模式使用vue-grid-layout实现,vue3版本的插件处于Beta阶段。使用v-model:layout双向绑定grid模式材质组件列表数据,因为材质数组存在vuex中,这里使用computedsetter更新。isLock用于判断当前是否处于编辑模式,锁定状态下禁止拖动和改变大小。目前使用的网格数为12,即屏幕宽度被分成12份。Fixed模式Fixed模式是使用作者自己开源的@howdjs/to-control插件完成的,可以让材质组件固定在页面任意位置,也支持拖动右下角改变大小。与网格模式不同,这里使用了事件回调函数更新组件的Vuex数据,isLock用于判断组件是否被锁定。该插件支持改变定位方向,记录右上角、右下角等,对于响应式布局非常有效。更多用法请参考:@howdjs/to-controlInteractivepop-upPopover系统提供了配置交互行为的功能,你可以配置点击一个组件时弹出另一个组件,并配置该组件的弹出方向成分。经过排查,发现Element-plus的Popover不适合这种情况,因为弹出组件是动态的。于是自己封装了一个组件,不仅支持全方位配置Popover,还扩展了一个ScreenCenter弹窗,让组件可以在屏幕中间弹出(类似dialog)。通过传入被点击的元素,目标弹窗的宽高,弹窗的方向,返回目标弹窗的x,y。核心代码如下:/***获取Popover目标信息*@paramelement来源DOM*@parampopoverRectpopover信息*@paramdirectionpopover方向*@returns[endX,endY,fromX,fromY]*/exportfunctiongetPopoverActivePointByDirection(element:HTMLElement,popoverRect:PopoverOption,direction=DirectionEnum.BOTTOM_CENTER){const{width,height,top,left}=element.getBoundingClientRect()const{width:popoverWidth,height:popoverHeight,offset=10}=popoverRect常量activePointMap={[DirectionEnum.SCREEN_CENTER]:[window.innerWidth/2-popoverWidth/2,window.innerHeight/2-popoverHeight/2],[DirectionEnum.TOP_START]:[left,top-popoverHeight-offset],[DirectionEnum.TOP_CENTER]:[left+width/2-popoverWidth/2,top-popoverHeight-offset],[DirectionEnum.TOP_END]:[left+width-popoverWidth,top-popoverHeight-offset],[DirectionEnum.RIGHT_START]:[left+width+offset,top],[DirectionEnum.RIGHT_CENTER]:[左+宽度+offset,top+height/2-popoverHeight/2],[DirectionEnum.RIGHT_END]:[left+width+offset,top+height-popoverHeight],[DirectionEnum.BOTTOM_END]:[left+width-popoverWidth,top+height]+offset],[DirectionEnum.BOTTOM_CENTER]:[left+width/2-popoverWidth/2,top+height+offset],[DirectionEnum.BOTTOM_START]:[left,top+height+offset],[DirectionEnum.LEFT_END]:[left-popoverWidth-offset,top+height-popoverHeight],[DirectionEnum.LEFT_CENTER]:[left-popoverWidth-offset,top+height/2-popoverHeight/2],[DirectionEnum.LEFT_START]:[left-popoverWidth-offset,top]}constfromPoint=[left+width/2,top+height/2]返回[...activePointMap[direction],...fromPoint]||[0,0,...fromPoint]}另外,使用属性transform-origin实现弹窗从被点击元素过渡的动画,最后配置弹窗的方向和类型弹出组件。代码参考:ActionPopover.vue获取任意网站的Favicon。在Collection和Search组件中,有一个功能可以在用户输入URL后自动获取网站的Favicon。在第一个版本中,直接使用urlorigin+/favicon.ico来获取,但经过多次尝试,发现很多网站的图标并不是以这种标准形式存储的。所以后来自己实现了一个后端接口来获取。后端接口原理:从用户输入的网站读取origin,尝试从Redis中读取缓存的图标路径,如果不在缓存中则返回,使用cheerio加载网站,使用$('link[rel*="icon"]').attr('href')读取图标路径。如果上一步没有读取到,继续尝试使用标准形式读取,即网站Origin+/favicon.ico成功读写Redis缓存,否则返回获取失败和接口接收type参数,后端可以直接返回图片流,解决部分网站ICON资源的CORS限制。因为在Collection组件中,为了减少初始访问请求的加载次数,前端读取图标后会将图标转成BASE64格式保存到本地存储。该方法需要使用Ajax获取图标,让接口直接返回文件流可以解决跨域问题。另外,在读取图标的时候,前端会使用Canvas通道的方式,让图标的白色部分透明。代码可以在这里找到。由于篇幅问题,使用的部分技术会不定期更新。心存感激的可以继续关注,Star,谢谢。相关链接Howdz介绍文档Github在线网页版地址