[{"content":" 文章在个人网站中发布，原文链接：Nuxt学习之旅1\n前言 在AI时代的冲击下，单纯的切图仔或者会一点前端工程化和系统化，已经不是什么值钱的技能了。\n为了不被时代滚滚的车轮轧到，决心趁着年轻学习一点全栈技术。而且目前中文Nuxt的教程还是较少的，所以就有了这套帖子，自己学习，也是帮助后来人少走弯路。\n安装Nuxt 我就直接使用WebStorm来建立项目了，作为初学者便利性和少踩坑还是应该在首位的。\n当然使用其它IDE建立项目也很简单，如下的命令就是使用当前最新nuxt版本创建项目：\n1 npm create nuxt@latest 你的项目名称 之后的流程无论使用什么IDE都一样，Nuxt会启动一个交互式的配置流程，完成之后就可以使用npm run dev启动开发服务器了。\n1 npm run dev 然后，咱们的第一个Nuxt项目就启动了！\n约定大于配置 Nuxt有个特点就是约定大于配置，理论上我们无需编写任何配置文件，Nuxt会按照约定来工作。例如我们无需配置路由，直接在pages目录下创建文件，Nuxt会自动识别并创建对应的路由。\n现在我们来尝试添加一个login页面：\n建立页面 我们在先app目录下建立一个pages目录，新建一个app/pages/index.vue，然后再建立app/pages/login.vue，并写上相应的内容。此时如果我们删掉app.vue，Nuxt就会把index.vue作为首页渲染出来了。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 \u0026lt;script lang=\u0026#34;ts\u0026#34;\u0026gt; import {defineComponent} from \u0026#39;vue\u0026#39; export default defineComponent({ name: \u0026#34;index\u0026#34; }) \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;!-- Nuxt用于页面跳转 --\u0026gt; \u0026lt;NuxtLink to=\u0026#34;/login\u0026#34;\u0026gt;前往登录页\u0026lt;/NuxtLink\u0026gt; \u0026lt;h2\u0026gt;我是首页\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style scoped\u0026gt; \u0026lt;/style\u0026gt; 默认布局 那app.vue是什么呢？他是我们的默认布局文件，也就是所有页面的父组件，我们把它删掉，Nuxt就会按照约定渲染index.vue。不过我们现在先把它加回来并编写一点布局：\n1 2 3 4 5 6 7 8 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;!-- app.vue是Nuxt约定的入口 --\u0026gt; \u0026lt;h1\u0026gt;这里是默认布局哦！\u0026lt;/h1\u0026gt; \u0026lt;!-- NuxtPage可以渲染下一级路由的内容 --\u0026gt; \u0026lt;NuxtPage /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; Nuxt发现app.vue存在就会使用它来渲染index.vue作为首页，现在我们的页面和项目结构就变成了这样:\n建立网络接口 Nuxt的网络接口也是约定式的，使用js语言来编写，对于咱们前端十分友好。\n我们在项目根目录下建立一个server目录（注意不是app目录下），然后建立server/api/hello.ts文件。那么Nuxt就会自动创建/api/hello的接口。\n我们写一个简单的接口用来计算两个数字的和：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export default defineEventHandler((event) =\u0026gt; { // 获取路由参数num1和num2 const query = getQuery(event) const num1 = Number(query.num1) const num2 = Number(query.num2) if(isNaN(num1) || isNaN(num2)) { // 如果参数不是数字，抛出错误 throw createError({ status: 500, statusText: \u0026#34;Invalid query number\u0026#34;, }) }else{ // 返回计算结果 return num1 + num2 } }) 前端使用接口 我们在app/pages/index.vue中调用这个接口试试看，这些代码乍一看可能难以理解，在后边我会分开解释。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 \u0026lt;script lang=\u0026#34;ts\u0026#34;\u0026gt; import {defineComponent} from \u0026#39;vue\u0026#39; export default defineComponent({ name: \u0026#34;index\u0026#34;, data(): any{ // 此处的数据由服务器处理完毕后将会随页面一起携带至前端 return { result: 0, num1: 0, num2: 0, } }, created(): any { // 这部分代码在服务器上执行 const route = useRoute() if(route.query.num1){ this.num1 = route.query.num1 } if(route.query.num2){ this.num2 = route.query.num2 } const data = useFetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) this.result = data.data }, methods: { // 这里的代码在浏览器执行 async plus(){ this.result = await $fetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) } } }) \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;!-- Nuxt用于页面跳转 --\u0026gt; \u0026lt;NuxtLink to=\u0026#34;/login\u0026#34;\u0026gt;前往登录页\u0026lt;/NuxtLink\u0026gt; \u0026lt;h2\u0026gt;我是首页\u0026lt;/h2\u0026gt; \u0026lt;input v-model=\u0026#34;num1\u0026#34;/\u0026gt; + \u0026lt;input v-model=\u0026#34;num2\u0026#34;/\u0026gt; = {{result}} \u0026lt;button @click=\u0026#34;plus\u0026#34;\u0026gt;计算\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style scoped\u0026gt; \u0026lt;/style\u0026gt; 我们先看服务器上处理的这部分：\n1 2 3 4 5 6 7 8 9 10 11 // 这部分代码在服务器上执行 // useRoute()可以获得路由参数，我们从这里获取num1和num2参数 const route = useRoute() if(route.query.num1){ this.num1 = route.query.num1 } if(route.query.num2){ this.num2 = route.query.num2 } // useFetch()用于在服务器内部调用接口 const data = useFetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) // 给data赋值，nuxt将会将它渲染到页面上 this.result = data.data 在Nuxt中，beforeCreate和created两个生命周期是在服务器上被运行的。我们可以像php那样预先在服务器组装好页面。\n可以看到，我们带参数访问页面时，服务器直接返回了计算好结果的页面，而不是SPA那样先获取一个空页面再填充数据。\n然后再看浏览器端的代码：\n1 2 3 4 5 6 7 8 9 methods: { // 这里的代码在浏览器执行 async plus(){ // $fetch()是Nuxt提供的数据请求函数 this.result = await $fetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) } } 这就和SPA没有区别了，将这个方法绑定到按钮上，就会在浏览器发起网络请求。\n小结 我们今天新建并体验了一把Nuxt。\n创建了两个页面和一个接口，并且在前后端分别使用了它。了解了Nuxt强大的服务端渲染能力，前后端无缝连接。\n","date":"2026-04-06T00:00:00Z","image":"https://blog.zhoujump.com/p/learn-nuxt-1/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/learn-nuxt-1/","title":"Nuxt 学习之旅 1"},{"content":" 文章在个人网站中发布，原文链接：删除具有大量部署的cloudflare pages项目\n部署量不是很大 如果你的部署量也就是一两百，可以尝试手动删除旧的部署，删除到部署数量小于一百一般就能删除了。 部署量很大 需要通过cloudflare的api删除部署，得安装cloudflare cli进行操作。\n这一部分也有其他人出过教程，这不做赘述,可以参考这篇文章.\n","date":"2026-01-02T00:00:00Z","image":"https://blog.zhoujump.com/p/delete-cloudflare-project/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/delete-cloudflare-project/","title":"删除具有大量部署的cloudflare pages项目"},{"content":" 文章在个人网站中发布，原文链接：记录一个纯CSS实现滚动驱动动画的效果\n先看一下这个简单的案例 代码： 你也可以直接划到下边看效果。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 \u0026lt;div class=\u0026#34;out-cont\u0026#34;\u0026gt; \u0026lt;!-- 最外层元素，用于框定元素需要固定的距离 --\u0026gt; \u0026lt;div class=\u0026#34;inner-cont\u0026#34;\u0026gt; \u0026lt;!-- 内层元素，用于固定元素的位置 --\u0026gt; \u0026lt;div class=\u0026#34;animation-item\u0026#34;\u0026gt; \u0026lt;!-- 播放动画的元素内容 --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .out-cont { /* 最外层元素，用于框定元素需要固定的距离，这里表示元素将被固定两个屏幕的高度 */ width: 100%; height: 200vh; position: relative; } .inner-cont { /* 内层元素，用于固定元素的位置，使用sticky使元素在范围内固定 */ width: 100%; height: 50vh; position: sticky; top: 25vh; display: flex; align-items: center; justify-content: center; } .animation-item{ /* 播放动画的元素内容，使用animation-timeline实现滚动驱动动画 */ width: 20%; aspect-ratio: 1; background-color: #2E74B5; animation: move 1s linear forwards; animation-timeline: view(); animation-range: contain; } @keyframes move { 0% { transform: rotate(0deg); border-radius: 8%; } 50% { transform: rotate(360deg); border-radius: 50%; } 100% { border-radius: 8%; transform: rotate(720deg); } } \u0026lt;/style\u0026gt; 效果： .out-cont .inner-cont 视口 sticky：粘性布局 对于前端码农来说这个玩意并不陌生，所以就只简单介绍一下：\nposition:sticky是介于position:relative与position:fixed之间的一种布局方式。当父元素出现在屏幕中时它表现得像fixed，会把自己固定在屏幕上。当父元素出现在屏幕外时它表现得像relative，会按照正常的文档流进行布局，被父容器带走。\n关于这个案例是如何使用sticky实现类似元素固定的效果的，上面效果示例右边有一个简单的展示，可以帮助理解。而且这个展示也是纯css的哦。\nsticky元素的所有父级都不能设置overflow:hidden，这样会使sicky无效，实在需要可以使用overflow:clip代替。\nanimation-timeline：滚动驱动动画 这是本案例的核心，元素上使用了这个属性，@keyframes定义的css动画将不会自动播放，而是根据滚动条的进度进行滚动。 这个属性有两个主要属性值：\nanimation-timeline: view()\n元素进入视口时开始播放动画，离开视口时结束。 animation-timeline: scroll()\n元素将在整个滚动容器中滚动时播放动画。 animation-timeline：cont-name\n命名容器，这个待会展开讲。 视口 view() 视口 scroll() animation-timeline: scroll() scroll()是对于整个页面的，相对而言比较简单，用于做一些与全局滚动相关的效果，例如文章阅读进度，这里引用前端侦探的一张图。 scroll()中可以填入两个参数：scroller和axis\nscroller\nscroller参数用于指定滚动容器，默认值为nearest。\n如果设置为nearest，将会使用最近的祖先滚动容器。\n如果设置为root，将会使用文档视口作为滚动容器。\n如果设置为self，将会使用元素本身作为滚动容器。\naxis\naxis参数用于指定滚动轴，默认值为block。\n如果设置为block，滚动容器的块级轴方向。 如果设置为inline，滚动容器内联轴方向。 如果设置为x，则元素将在水平方向上滚动。\n如果设置为y，则元素将在垂直方向上滚动。\nanimation-range 如果我不想让动画整个滚动期间都播放呢，可以使用animation-range属性设置动画的起止位置，px，%单位均可。\n例如:\n1 2 3 .animation{ animation-range: 0 100px; } 这样动画就只会在滚动容器滚动到0到100px的位置时播放,在100px之后就不会再播放了。\nanimation-timeline：view() view()是相对于元素与视口的位置的，这个案例就是基于view()的。\nview()中也可以填入两个参数：axios和inset\naxis\naxis参数用于指定滚动轴，默认值为block。\n如果设置为block，滚动容器的块级轴方向。 如果设置为inline，滚动容器内联轴方向。 如果设置为x，则元素将在水平方向上滚动。\n如果设置为y，则元素将在垂直方向上滚动。\ninset\ninset参数用于规定动画从何时开始何时结束，是元素刚冒尖尖就开始还是元素完全进入视口呢，就靠这个属性控制,有点类似于上面animation-range的作用。\ninset接受一个或两个值，两个值时代表开始位置和结束位置，px，%单位均可\nanimation-timeline：cont-name 你可能发现了，仅靠上面那些属性，只能实现父元素滚动驱动子元素的动画，要是我需要滚动一个容器去驱动另一个兄弟元素的动画呢？就需要靠命名的容器实现了。\n使用很简单，就是在滚动容器上使用一个属性scroll-timeline-name。\n1 2 3 4 5 6 7 8 .scroll{ /* 命名滚动容器 */ scroll-timeline-name: --my-scroller; } .animation{ /* 需要被驱动动画的元素 */ animation-timeline: --my-scroller; } 这样.scroll容器被滚动时，.animation元素就会根据滚动容器的滚动进度进行动画播放。\n相关知识 position:fixed\nanimation-timeline\nanimation-range\n前端侦探\n","date":"2025-09-14T00:00:00Z","image":"https://blog.zhoujump.com/p/animation-timeline/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/animation-timeline/","title":"记录一个纯CSS实现滚动驱动动画的效果"},{"content":" 文章在个人网站中发布，原文链接：在vue中contenteditable元素的双向数据绑定\n记录一下在vue中使用contenteditable属性所需要注意的地方。\ncontenteditable 用途 给任何元素加上contenteditable=\u0026ldquo;true\u0026quot;属性，就可以让元素变为可编辑状态。可以实现比普通input、textarea更丰富编辑控件排版。例如这种可以嵌入tag的编辑框。 特点 仅添加contenteditable属性而不赋予值，将视为contenteditable=\u0026ldquo;false\u0026rdquo;。\n若contenteditable=\u0026ldquo;true\u0026quot;的容器中存在contenteditable=\u0026ldquo;false\u0026quot;的元素，那么那个元素本身将不能编辑，不过按下退格能将整个元素一起删除。\n若contenteditable=\u0026ldquo;true\u0026quot;的容器中存在普通的元素，那么按退格将按html的结构删除文本。直到删除至最里层的元素没有文字之后，将会删除元素标签本身。\n关于Vue的绑定 v-model v-model本质是自动绑定input事件和value属性。虽然contenteditable将赋予元素input事件，但是既然都需要contenteditable属性了，这个元素大概率是没有value属性的。所以大部分情况下v-model只能将元素内容给到vue，但vue无法更新元素的内容。\n这时候如果理所当然的去绑定值：\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; v-model=\u0026#34;item.value\u0026#34; v-html=\u0026#34;item.value\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 或者\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; v-model=\u0026#34;item.value\u0026#34;\u0026gt;{{item.value}}\u0026lt;/span\u0026gt; 就会发现根本无法正常输入。每次按下按键后，光标将会回到元素开头。如果是使用拼音输入法，问题将会更加鬼畜。 这是因为input事件是每次按键都会触发的，每次按键都会把当前所见的所有内容给到vue处理。同时模板语法{{}}或者v-html标记的内容将会被vue更新，整个元素的内容都会被替换，自然无法正常输入了。\n解决方法很简单，我们不要使用v-model或者v-input来更新数据了，应该挑一个更合适的时机来更新数据，例如输入框失焦时更新数据。\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; @blur=\u0026#34;item.value=$event.target.innerText\u0026#34;\u0026gt;{{item.value}}\u0026lt;/span\u0026gt; 不出意外输入框此时就能正常双向绑定数据了\n相关知识 contenteditable\nv-model\nblur\n","date":"2025-08-26T00:00:00Z","image":"https://blog.zhoujump.com/p/contenteditable-vue/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/contenteditable-vue/","title":"在vue中contenteditable元素的双向数据绑定"},{"content":" 文章在个人网站中发布，原文链接：如何向页面中嵌入UnityWeb3D\n随着现在设备性能的提升，向网站中插入3D模型已经不是啥稀奇事情了。而且插入3D元素能极高地提高用户体验，再加上一些用户交互就能给人非常惊艳的体验。\n目前比较流行的Web3D技术有three.js、还有可视化编辑模型与交互的spline，不过three.js的学习难度和spline的高昂价格已经足够让一部分人望而却步了。\n那有没有更经济和简单的技术呢？有的有的，兄弟有的，UnityWeb3D也许就是一个不错的选择。\n工程准备 Unity的场景搭建和后处理的添加非常简单，无需书写代码和着色器。具体操作方式B站大学有超级多教程可以学习，我这里准备了一个地球然后给它加了一些滤镜。 交互也是非常简单，通过可视化脚本写一些交互逻辑就可以了。具体也可以去B站大学选修。\n准备好工程后依次选择编辑\u0026gt;生成设置\u0026gt;WebGL\u0026gt;切换平台，切换项目至WebGL平台，期间unity会走一次进度条。我这里已经切换过了，所以显示的是生成按钮。\n等待切换完成后可以对生成设置进行一些配置，选择编辑\u0026gt;项目设置\u0026gt;玩家\u0026gt;H5。根据自己的需求来选择配置。unity默认使用了gzip压缩，可以大幅度降低生成大小，只是我们需要在服务器上配置gzip。嫌麻烦而且不缺流量带宽的富哥可以关掉。 然后点击生成，unity会让你选择一个文件夹来保存生成的文件。我这里新建了一个dist文件夹用来放置生成的文件。等待unity生成完成后，会生成一个index.html和一个Build文件夹。 嵌入UnityWeb3D 咱们把Build文件夹中所有文件都上传至服务器上，总共是四个文件，这里我是放在了assets/unityAnimation文件夹下。 然后页面这边先引入刚刚上传的dist.loader.js文件，然后再准备一个canvas元素，并给予一个id，方便咱们在js中获取到它。\n1 2 \u0026lt;script src=\u0026#34;/assets/unityAnimation/dist.loader.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;canvas id=\u0026#34;unity-canvas\u0026#34;\u0026gt;\u0026lt;/canvas\u0026gt; 然后使用createUnityInstance()函数来创建UnityWeb3D实例。配置中这些路径都换成自己上传的文件路径就可以使用了，其它的配置在官方文档：WebGL templates处可以查看。\n因为我开启了gzip压缩，所以这里需要加上.gz后缀。如果你没有在生成设置开启gzip就不需要加后缀而且做完这步刷新页面就会发现模型正确加载了。如果开启了gzip则服务器上还需要再配置一下gzip。\n1 2 3 4 5 createUnityInstance(document.querySelector(\u0026#34;#unity-canvas\u0026#34;), { dataUrl: \u0026#34;/assets/unityAnimation/dist.data.gz\u0026#34;, frameworkUrl: \u0026#34;/assets/unityAnimation/dist.framework.js.gz\u0026#34;, codeUrl: \u0026#34;/assets/unityAnimation/dist.wasm.gz\u0026#34;, }); 我的服务端是使用的Nginx，所以需要在Nginx的配置文件中添加如下代码来开启gzip。使用gzip的好处是减小资源大小，我这个地球压缩前有足足30M，压缩之后只有8M了，可谓是非常amazing啊。\n当然如果你选择了其它的压缩方式或者服务端，在官方文档：WebGL：服务器配置代码示例也有详细的说明。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 location ~ .+\\.(data|symbols\\.json)\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/octet-stream; } location ~ .+\\.js\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/javascript; } location ~ .+\\.wasm\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/wasm; } 看看效果 就这样，我一个完全不懂Unity3D和C#的前端人也能做出这样超级炫酷的效果啦。 ","date":"2025-04-18T00:00:00Z","image":"https://blog.zhoujump.com/p/insert-unity/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/insert-unity/","title":"如何向页面中嵌入UnityWeb3D"},{"content":" 文章在个人网站中发布，原文链接：自己写一个小小vue - getter与setter\n我相信各位前端大佬和小佬们都听过这句话：\nvue2使用getter与setter来控制数据，vue3则直接使用proxy。\n肯定有很多人像我一样，只管爽用vue就行，我管你什么getter、proxy的。不过这个东西在实际开发中是非常实用的，可以实现一些非常优雅的封装。不过首先，让我们先了解一下这俩东西。\ngetter与setter 这俩东西就相当于一个监听器，当某个数据被修改或者被读取时，你设置的getter和setter就会运行。\n例如我现在定义了一个box(盒子)，里边的content(内容)是一个‘banana🍌’\n1 2 3 let box = { content: \u0026#39;banana🍌\u0026#39; } 我们试试监听这个盒子\n1 2 3 4 5 6 7 8 9 10 11 12 let box = { content: \u0026#39;banana🍌\u0026#39;, // 定义getter非常简单,就是一个函数,函数名就是需要被监听的变量 get boxContent() { alert(\u0026#39;有人查看了盒子里的东西，是个\u0026#39;+this.content) }, // 定义setter也是一样 set boxContent(newContent) { this.content = newContent alert(\u0026#39;有人换掉了盒子里的东西，换成了\u0026#39;+newContent) } } 这里有个需要注意的点就是getter和setter的名字不能和原变量相同,不过在firefox浏览器中却是可以这么写的,不过鉴于兼容性原因我们不建议这么写\n这时候一旦有人尝试读取和修改boxContent，就会触发getter和setter。\n1 2 3 4 5 //如果盒子里有东西 if(box.boxContent){ //就把它换成苹果 box.boxContent = \u0026#39;apple🍎\u0026#39; } 第一行的if读取了box.boxContent，触发getter，然后弹出提示。 紧接我们修改了box.boxContent，触发setter，然后弹出提示。 好了，你已经学会了getter和setter了，让我们来实现一个vue把。\n实现一个小小vue 来个经典案例,小黑记事本:\n样式布局我不多赘述，都挺简单的。具体代码可以上码上掘金或者来github查看\n说是实现小小vue，实际上只是通过getter和setter来尝试实现v-model的功能，毕竟咱也不是尤雨溪，咱就来简单点的。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;style.css\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Demo\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;outter\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;title\u0026#34;\u0026gt;Demo\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;list\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;item\u0026#34;\u0026gt; \u0026lt;span\u0026gt;2333\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026#34;delete\u0026#34;\u0026gt;Delete\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;add\u0026#34;\u0026gt; \u0026lt;!-- 我们设置一个bind属性，它就相当于v-model，绑定到input变量 --\u0026gt; \u0026lt;input bind=\u0026#34;input\u0026#34;/\u0026gt; \u0026lt;button onclick=\u0026#34;add()\u0026#34; class=\u0026#34;add-btn\u0026#34;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- 这里也来一个bind，也绑定到input变量 --\u0026gt; \u0026lt;div\u0026gt;input的值:\u0026lt;span bind=\u0026#34;input\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script src=\u0026#34;index.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; js部分如下，我们只关注前面部分\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 // 我们先定义好数据 let data = { list:[], input:\u0026#39;\u0026#39; } // 实现绑定 // 此处获得所有拥有bind属性的元素 let binds = document.querySelectorAll(\u0026#39;[bind]\u0026#39;); // 遍历这些元素 binds.forEach((item) =\u0026gt; { // 绑定input事件 item.oninput = (e) =\u0026gt; { // 当input时更新其值 data[item.getAttribute(\u0026#39;bind\u0026#39;)] = e.target.value } }) // 实现数据监听 let myData = {} // 因为getter和setter不能和原变量同名，咱把原变量包裹起来一下 myData.data = data // 我们遍历data里的每个对象 for(let item in data){ //为每个item都定义一个getter和setter Object.defineProperty(myData, item, { // 这里通过defineProperty来设置getter和setter get(){ return myData.data[item] }, set(val){ myData.data[item] = val // 当值更新时，我们遍历所有有bind属性的元素 binds.forEach((bindsItem) =\u0026gt; { // 如果这个元素有bind属性，并且属性名等于item，就修改它的值 if(bindsItem.getAttribute(\u0026#39;bind\u0026#39;) === item){ bindsItem.value = val bindsItem.innerHTML = val } }) // 当 list 更新时，重新渲染列表 if(item === \u0026#39;list\u0026#39;) { renderList() } } }) } // 把myData赋值给data，这样我们的data就变成一个代理对象了 data = myData //之后的代码感兴趣的可以看看 // 渲染列表函数 function renderList() { const listContainer = document.querySelector(\u0026#39;.list\u0026#39;) listContainer.innerHTML = data.list.map((item, index) =\u0026gt; ` \u0026lt;div class=\u0026#34;item\u0026#34;\u0026gt; \u0026lt;span\u0026gt;${item}\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026#34;delete\u0026#34; onclick=\u0026#34;deleteItem(${index})\u0026#34;\u0026gt;Delete\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; `).join(\u0026#39;\u0026#39;) } // 添加项目函数 function add() { if(data.input.trim()) { data.list = [...data.list, data.input] data.input = \u0026#39;\u0026#39; // 清空输入框 } } // 删除项目函数 function deleteItem(index) { data.list = data.list.filter((_, i) =\u0026gt; i !== index) } 看看效果 这样我们就用原生js实现了一个v-modle，是不是成就感满满呢 相关知识 getter setter defineProperty\n","date":"2025-03-13T00:00:00Z","image":"https://blog.zhoujump.com/p/getter-setter/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/getter-setter/","title":"自己写一个小小vue - getter与setter"},{"content":" 文章在个人网站中发布，原文链接：纯css实现跑马灯效果\n先看效果 我是练习市场两年半的个人实习生，喜欢html、css、js。 我是练习市场两年半的个人实习生，喜欢html、css、js。 \u0026lt;marquee\u0026gt;标签 这时候一些前端大佬就会站起来说了，这不就是一个\u0026lt;marquee\u0026gt;标签的事吗？\n不过这个元素目前已经被废弃了，虽然还能继续使用，但保不齐哪天就被主流浏览器移除了。我们还是少用为妙 使用css动画 为文字应用动画 既然跑马灯只是简单的将文本水平滚动，那么我们可以使用css动画来实现。 我们先设置一段文本，然后为其应用动画。\n我是练习市场两年半的个人实习生，喜欢html、css、js。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;div class=\u0026#34;marquee-1\u0026#34;\u0026gt; \u0026lt;span\u0026gt;我是练习市场两年半的个人实习生，喜欢html、css、js。\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee-1 { overflow: hidden; width: 300px; display: flex; } .marquee-1 span { animation: marquee 10s linear infinite; } @keyframes marquee { 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; 看起来文字在超出容器后折行了。\n强制使文字不换行 然后我们强制使文字不换行。看看效果\n我是练习市场两年半的个人实习生，喜欢html、css、js。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;marquee-2\u0026#34;\u0026gt; \u0026lt;span\u0026gt;我是练习市场两年半的个人实习生，喜欢html、css、js。\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee-2 { overflow: hidden; /* 这里可以强制使文字不换行 */ white-space: nowrap; width: 300px; display: flex; } .marquee-2 span { animation: marquee 10s linear infinite; } @keyframes marquee { 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; 现在看起来有那么点意思了，就是文字完全滚完之后才会重新开始滚动。\n复制一份元素来实现无缝滚动 我们可以复制一份元素，然后让动画同时进行，这样看起来就像在无缝滚动了。完整代码在下边。\n我是练习市场两年半的个人实习生，喜欢html、css、js。 我是练习市场两年半的个人实习生，喜欢html、css、js。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;marquee\u0026#34;\u0026gt; \u0026lt;span\u0026gt;我是练习市场两年半的个人实习生，喜欢html、css、js。\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;我是练习市场两年半的个人实习生，喜欢html、css、js。\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee { overflow: hidden; white-space: nowrap; width: 400px; display: flex; } .marquee span { animation: marquee 10s linear infinite; } @keyframes marquee { 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; 相关知识 marquee：跑马灯元素 white-space\n","date":"2025-02-20T00:00:00Z","image":"https://blog.zhoujump.com/p/css-marquee/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/css-marquee/","title":"纯css实现跑马灯效果"},{"content":" 文章在个人网站中发布，原文链接：记一次将博客网站迁移\n起因 因为某一天在gitlab上号时发现被下发了60天死亡通知，账号会在两个月后被删除，所以网站搬家就被提上了日程。\n当然氪金是不可能氪金的，于是把主意打到了赛博活佛cloudflare身上。\n步骤 转移仓库 gitlab想要删我的仓库，当务之急当然是先转移仓库，我这里是转移至github。过程很简单，选择导入仓库，然后填写原仓库的地址和账号密码即可。 配置Cloudflare Pages 操作也是非常简单。提前准备一个cloudflare账号，打开pages页面，然后链接到git，进行登录。 然后选择相应的仓库，选择对应的框架预设，然后点击下一步就会开始构建。\n如果构建发生错误，有可能是hugo版本的问题，添加一个名为HUGO_VERSION的环境变量，填入你目前正在使用的hugo版本即可。\n配置域名 构建完成后就能看到这个pages了，进入配置页，选择 自定义域 ，点击 设置自定义域 然后输入域名即可。因为我的域名就在cloudflare，所以可以直接生效，若域名在其它域名商那，按照提示添加一个cname解析就好。\n至此，转移工作已经完成，还是原来的域名，原来的网站，但是网站已经悄悄迁移了。\n","date":"2025-02-04T00:00:00Z","image":"https://blog.zhoujump.com/p/gitlab-to-cloudflare/cover.webp","permalink":"https://blog.zhoujump.com/p/gitlab-to-cloudflare/","title":"记一次将博客网站迁移"},{"content":" 文章在个人网站中发布，原文链接：纯css实现环形进度条\n先看效果 这个环形进度条使用纯css制作，使用css变量控制进度，你可以打开开发者工具选中它改变它内联的css变量--progress: 60，进度和内容均会发生变化。\nCSS变量 CSS变量是css3引入的新特性，它允许你定义一个变量。你可以通过--变量名来定义一个css变量，通过var(--变量名)来使用它。 下边这个小demo展示了如何使用css变量。你可以打开开发者工具摆弄一下。\n我的颜色由css变量控制 1 2 3 4 5 6 7 8 9 10 \u0026lt;!-- 在style内定义了css变量color --\u0026gt; \u0026lt;div class=\u0026#34;demo-var\u0026#34; style=\u0026#34;--color:red\u0026#34;\u0026gt; 我的颜色由css变量控制 \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-var{ /* 在此处使用color */ color: var(--color); } \u0026lt;/style\u0026gt; 我们此处使用了一个小技巧。通过css计数器来显示css变量，环形中间的数字就是这样显示的，你可以打开开发者工具摆弄一下下边的小demo。\n1 2 3 4 5 6 7 8 9 \u0026lt;div class=\u0026#34;demo-var\u0026#34; style=\u0026#34;--num:100\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-num::after{ /* 将num计数器重置为你设置的css变量 */ counter-reset: num var(--num); /* 再使用计数器，就变相显示了它，不过这种方法只能显示数字 */ content: \u0026#39;num的值是：\u0026#39;counter(num); } \u0026lt;/style\u0026gt; 锥型渐变 使用锥型渐变可以产生一个披萨形状，这是我们环形进度条的关键。\n1 2 3 4 5 6 7 8 9 10 11 12 13 \u0026lt;!-- 我们照葫芦画瓢定义一个css变量 --\u0026gt; \u0026lt;div class=\u0026#34;demo-conic\u0026#34; style=\u0026#34;--progress:60\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-conic{ margin: auto; position: relative; width: 200px; height: 200px; border-radius: 50%; /* 在此处使用css变量，通过calc(var(--progress) * 1%)将其转化为百分比 */ background: conic-gradient( #99e6ff 0%,#99e6ff calc(var(--progress) * 1%),transparent 0%); } \u0026lt;/style\u0026gt; 接下来要做的就很简单了，我们使用一个白色的圆来覆盖锥型渐变，再通过前边说的小技巧显示进度数字，这样我们就可以得到一个环形进度条了。\n完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 \u0026lt;!-- 创建进度条容器并定义css变量 --\u0026gt; \u0026lt;div class=\u0026#34;demo-process\u0026#34; style=\u0026#34;--progress: 60\u0026#34;\u0026gt; \u0026lt;!-- 内部再来个圆遮住披萨形成环形 --\u0026gt; \u0026lt;div class=\u0026#34;demo-process-inner\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-process{ margin: auto; position: relative; width: 200px; height: 200px; border-radius: 50%; padding: 16px; /* 用css变量产生一个披萨形 */ background: conic-gradient( #99e6ff 0%,#99e6ff calc(var(--progress) * 1%),transparent 0%); } .demo-process::after{ /* 我们弄一个小圆来美化进度条 */ content: \u0026#39;\u0026#39;; position: absolute; left: calc(50% - 10px); top: calc(50% - 10px); width: 16px; height: 16px; border-radius: 50%; /* 通过css变量控制小圆的位置，使他跟随进度 */ transform: rotate(calc(3.6deg * var(--progress))) translateY(-92px); /* background: white; 此处css变量是为了适配夜间模式，你直接使用白色即可*/ background: var(--card-background); border: 4px solid #99e6ff;; } .demo-process::before{ /* 此处也是用于美化进度条 */ content: \u0026#39;\u0026#39;; position: absolute; left: calc(50% - 8px); top: 0; width: 16px; height: 16px; border-radius: 50%; background: #99e6ff; } .demo-process-inner{ /* 内部的用于遮住披萨的圆 */ width: 100%; height: 100%; border-radius: 50%; display: flex; justify-content: center; align-items: center; /* background: white; 此处也是为了适配夜间模式，你直接使用白色即可*/ background: var(--card-background); } .demo-process-inner::before{ /* 显示进度的数字 */ counter-reset: process var(--progress); content: counter(process)\u0026#39;%\u0026#39;; font-size: 30px; color: #99e6ff; } \u0026lt;/style\u0026gt; 相关知识 css变量 锥型渐变 css计数器\n","date":"2024-12-15T00:00:00Z","image":"https://blog.zhoujump.com/p/circular-progression/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/circular-progression/","title":"纯css实现环形进度条"},{"content":" 文章在个人网站中发布，原文链接：纯CSS实现一个炫酷的输入框效果\n先看效果 username :valid与:invalid伪类 这两个伪类可以用于input元素和select元素，用于选中校验通过和未校验通过的输入框。我们通过下边这个简单的小案例了解一下它们：\n这个输入框会校验输入是否是个电子邮箱地址，否则背景颜色将红色警告。\n代码也非常简单：\n1 2 3 4 5 \u0026lt;input class=\u0026#34;demo-input-2\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; //invalid伪类可以选中不能通过校验的input .demo-input-2:invalid{background:red} \u0026lt;/style\u0026gt; required属性 这个属性可以用在input，select元素上，表示这个输入框是必填的。如果留空，将无法通过校验，可以被:invalid伪类选中，否则被:valid选中，例如：\n这个输入框不能留空，否则背景颜色将红色警告。\n1 2 3 4 \u0026lt;input class=\u0026#34;demo-input-3\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; .demo-input-2:invalid{background:red} \u0026lt;/style\u0026gt; pattern属性 这个属性可以用在input，select元素上，它的属性值需要填入一个正则表达式。如果输入框内容不符合正则表达式，可以被:invalid伪类选中，否则被:valid选中，例如：\n这个输入框只能输入六位数字，否则背景颜色将红色警告。\n1 2 3 4 \u0026lt;input class=\u0026#34;demo-input-4\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; .demo-input-4:invalid{background:red} \u0026lt;/style\u0026gt; 完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 \u0026lt;div class=\u0026#34;demo-input\u0026#34;\u0026gt; \u0026lt;input required/\u0026gt; \u0026lt;label\u0026gt;username\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-input{ position: relative; height: 40px; width: 300px; margin: 8px; } .demo-input *{transition-duration: 100ms} .demo-input input{ width: 100%; height: 100%; box-sizing: border-box; border: 2px solid gray; border-radius: 6px; outline: none; padding-left: 10px; font-size: 20px; color: #006080; } .demo-input input:valid, .demo-input input:focus {border: 2px solid #006080} .demo-input label{ position: absolute; top:0; left: 10px; font-size: 20px; color: grey; line-height: 36px; pointer-events: none; } .demo-input input:valid + label, .demo-input input:focus + label { color: #006080; top:-12px; font-size: 18px; padding: 0 4px; background-color: white; line-height: 18px; } \u0026lt;/style\u0026gt; 相关知识 表单数据校验\n:valid伪类\n:invalid伪类\nrequired属性\npattern属性\n","date":"2024-10-22T00:00:00Z","image":"https://blog.zhoujump.com/p/valid_input/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/valid_input/","title":"纯CSS实现一个炫酷的输入框效果"},{"content":" 文章在个人网站中发布，原文链接：巧用属性选择器纯CSS实现筛选器\n先看看这个样例的效果 所有 黄色 红色 绿色 紫色 🍉🍊🍈 🍇🥝🍋 ‍🍌🍍🥭 🍎🍏🍐 🍑🍒🍓 🫐‍🍅🫒 :has()伪类与属性选择器 我们通过下边这个小案例来了解一下这俩选择器\n属性选择器 我是 我是 1 2 3 4 5 6 7 \u0026lt;!-- 鼠标摸在它们上边会被添加伪元素 --\u0026gt; \u0026lt;div linkto=\u0026#39;box1\u0026#39;\u0026gt;我是\u0026lt;/div\u0026gt; \u0026lt;div linkto=\u0026#39;box2\u0026#39;\u0026gt;我是\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; div[linkto=\u0026#39;box1\u0026#39;]:hover::after{content:\u0026#39;box1\u0026#39;} div[linkto=\u0026#39;box2\u0026#39;]:hover::after{content:\u0026#39;box2\u0026#39;} \u0026lt;/style\u0026gt; 在这个例子中，两个div没有任何类名，不过都被赋予了一个自定义属性linkto。我们可以借此通过[linkto='box1']来选中他们，赋予伪元素。\n如何使用 除了这个例子中的完全匹配某个属性值，属性选择器还有很多其它用法：\n匹配属性名称 img[alt]\n这个选择器会选择所有拥有alt属性的img元素，无论alt内容是什么。\n完全匹配属性 img[hidden=\u0026quot;true\u0026quot;]\n这个选择器会选择所有拥有hidden属性并且属性值为true的img元素，我们为这个选择器添加display：none样式就能方便的隐藏元素了。\n匹配存在某个属性值 img[tag~=\u0026quot;hd\u0026quot;]\n这个选择器能找到所有拥有tag属性且存在hd这个属性值的img元素，例如上边这个选择器能选中\u0026lt;img tag=\u0026quot;hd cover\u0026quot;/\u0026gt;、\u0026lt;img tag=\u0026quot;sport football hd\u0026quot;/\u0026gt;这些元素。\n匹配拥有某个前缀的属性值 匹配前缀有两种方式： span[lang|=\u0026quot;zh\u0026quot;]\n这个选择器能选择所有lang属性值以\u0026rsquo;zh-\u0026lsquo;开头的span元素，例如\u0026lt;span lang=\u0026quot;zh-TW\u0026quot;\u0026gt;、\u0026lt;span lang=\u0026quot;zh-CN\u0026quot;\u0026gt;。\nimg[type^=\u0026quot;low\u0026quot;]\n这个选择器可以选择所有type属性以\u0026rsquo;low\u0026rsquo;开头的img元素，例如\u0026lt;img type=\u0026quot;lowPower\u0026quot;/\u0026gt;、\u0026lt;img type=\u0026quot;lowLevel\u0026quot;/\u0026gt;。它与上边的区别是可以匹配不使用\u0026rsquo;-\u0026lsquo;来分割的单词。\n匹配拥有某个后缀的属性值 img[type$=\u0026quot;ball\u0026quot;]\n这个选择器可以选择所有type属性以\u0026rsquo;ball\u0026rsquo;结尾的img元素，例如\u0026lt;img type=\u0026quot;football\u0026quot;/\u0026gt;、\u0026lt;img type=\u0026quot;basketball\u0026quot;/\u0026gt;。\n匹配含有某个字符串的属性值 img[type*=\u0026quot;0\u0026quot;]\n这个选择器可以选择所有type属性包含'0\u0026rsquo;的img元素，例如\u0026lt;img type=\u0026quot;110\u0026quot;/\u0026gt;、\u0026lt;img type=\u0026quot;4008208820\u0026quot;/\u0026gt;。\n:has()伪类 我是 我是 1 2 3 4 5 6 7 8 9 10 \u0026lt;div class=\u0026#34;cont-box\u0026#34;\u0026gt; \u0026lt;!-- 摸在这些盒子上，下边的disply-box将会显示伪元素 --\u0026gt; \u0026lt;div linkto=\u0026#39;box3\u0026#39;\u0026gt;我是\u0026lt;/div\u0026gt; \u0026lt;div linkto=\u0026#39;box4\u0026#39;\u0026gt;我是\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;disply-box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont-box:has(\u0026gt;[linkto=\u0026#34;box3\u0026#34;]:hover) + .disply-box::after{content:\u0026#39;box3\u0026#39;} .cont-box:has(\u0026gt;[linkto=\u0026#34;box4\u0026#34;]:hover) + .disply-box::after{content:\u0026#39;box4\u0026#39;} \u0026lt;/style\u0026gt; 因为被hover的元素被cont-box包裹了，直接通过后续兄弟选择器无法选择到父亲的兄弟。我们可以通过给父亲:has()伪类来间接选择。\n关于:has()选择器 当括号内的选择器成立时，该伪类就会生效。\nh1:has(+ p)\n这个例子中，将相邻兄弟选择器放入。当某个h1标签后边紧跟着一个p标签，那么这个伪类就会生效。这样你可以给p标签前边的h1标签赋予样式而其它h1标签不受影响。\n.cont-box:has(\u0026gt;[linkto=\u0026quot;box4\u0026quot;]:hover)\n我们解读一下例子里的选择器;括号里的内容是\u0026gt;[linkto=\u0026quot;box4\u0026quot;]:hover，意思是存在某个子元素，这个子元素的linkto属性值为box4，并且正在被鼠标摸着。再搭配:has()伪类，就是找到存在这么个子元素的.cont-box，为其赋予样式。\n完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 \u0026lt;div class=\u0026#34;filter\u0026#34;\u0026gt; \u0026lt;input checked type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;all\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;all\u0026#34;\u0026gt;所有\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;yellow\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;yellow\u0026#34;\u0026gt;黄色\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;red\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;red\u0026#34;\u0026gt;红色\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;green\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;green\u0026#34;\u0026gt;绿色\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;purple\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;purple\u0026#34;\u0026gt;紫色\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;fruit-cont\u0026#34;\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍉\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍊\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍈\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;p\u0026#34;\u0026gt;🍇\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🥝\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍋\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;‍🍌\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍍\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🥭\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍎\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍏\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍐\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍑\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍒\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍓\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;p\u0026#34;\u0026gt;🫐\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;‍🍅\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🫒\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .filter{display: flex;cursor: pointer;user-select: none;} .filter label{ width: calc(25% - 12px); box-sizing: border-box; margin: 6px; height: 40px; line-height: 36px; text-align: center; border-radius: 8px; border: 2px solid; } .filter input{display: none;} .fruit-cont{display: flex;flex-wrap: wrap;} .fruit-cont div{ display: none; width: calc(25% - 12px); box-sizing: border-box; margin: 6px; height: 60px; line-height: 60px; text-align: center; font-size: 30px; text-shadow: 0 0 6px white; border-radius: 8px; } .filter:has(\u0026gt;#yellow:checked) ~ .fruit-cont div[c=\u0026#34;y\u0026#34;]{display: block} .filter:has(\u0026gt;#green:checked) ~ .fruit-cont div[c=\u0026#34;g\u0026#34;]{display: block} .filter:has(\u0026gt;#red:checked) ~ .fruit-cont div[c=\u0026#34;r\u0026#34;]{display: block} .filter:has(\u0026gt;#purple:checked) ~ .fruit-cont div[c=\u0026#34;p\u0026#34;]{display: block} .filter:has(\u0026gt;#all:checked) ~ .fruit-cont div{display: block} .fruit-cont div[c=\u0026#34;r\u0026#34;]{background-color: #e74c3c} .fruit-cont div[c=\u0026#34;g\u0026#34;]{background-color: #2ecc71} .fruit-cont div[c=\u0026#34;p\u0026#34;]{background-color: #9b59b6} .fruit-cont div[c=\u0026#34;y\u0026#34;]{background-color: #f39c12 } .filter label[for=\u0026#34;red\u0026#34;]{border-color: #e74c3c;color: #e74c3c;} .filter label[for=\u0026#34;green\u0026#34;]{border-color: #2ecc71;color: #2ecc71;} .filter label[for=\u0026#34;purple\u0026#34;]{border-color: #9b59b6;color: #9b59b6;} .filter label[for=\u0026#34;yellow\u0026#34;]{border-color: #f39c12 ;color: #f39c12 ;} .filter label[for=\u0026#34;all\u0026#34;]{border-color: black;color: black ;} .filter input:checked + label[for=\u0026#34;red\u0026#34;]{background-color: #e74c3c;color: white;} .filter input:checked + label[for=\u0026#34;green\u0026#34;]{background-color: #2ecc71;color: white;} .filter input:checked + label[for=\u0026#34;purple\u0026#34;]{background-color: #9b59b6;color: white;} .filter input:checked + label[for=\u0026#34;yellow\u0026#34;]{background-color: #f39c12 ;color: white;} .filter input:checked + label[for=\u0026#34;all\u0026#34;]{background-color: black ;color: white;} \u0026lt;/style\u0026gt; 相关知识 :has()伪类 属性选择器\n","date":"2024-10-18T00:00:00Z","image":"https://blog.zhoujump.com/p/checkbox-filter/cover.zh-cn.webp","permalink":"https://blog.zhoujump.com/p/checkbox-filter/","title":"巧用属性选择器纯CSS实现筛选器"},{"content":" 文章在个人网站中发布，原文链接：我的简历编辑器上线了\n简历布局自由拖拽，内置两套简洁美观的主题可以自由切换，可以导出为图片或者直接调用打印。免费使用，无需登录。欢迎使用。\n立即进入 ","date":"2024-10-07T00:00:00Z","image":"https://blog.zhoujump.com/p/resume-tool-index/cover.webp","permalink":"https://blog.zhoujump.com/p/resume-tool-index/","title":"我的简历编辑器上线了"},{"content":" 文章在个人网站中发布，原文链接：纯CSS实现输入框的编辑和保存按钮\n先看效果 点击右侧编辑激活左侧input,点击右侧完成保存输入框内容\npointer-events 我们都知道可以通过\u0026lt;input disabled=\u0026quot;true\u0026quot;/\u0026gt;中disabled属性来使输入框不可编辑，但是它作为元素属性需要使用js来修改。那我们纯css环境如何实现呢？ 答案是使用pointer-events属性，它用来定义元素如何响应用户的点击。例如下边这两个例子：\npointer-events: auto pointer-events: none\n1 2 3 4 5 6 7 8 \u0026lt;!-- 这个a标签设置了pointer-events: auto --\u0026gt; \u0026lt;a style=\u0026#34;pointer-events: auto;\u0026#34; href=\u0026#39;https://blog.zhoujump.com\u0026#39; target=\u0026#34;_blank\u0026#34;\u0026gt;pointer-events: auto\u0026lt;/a\u0026gt; \u0026lt;!-- 这个a标签设置了pointer-events: auto --\u0026gt; \u0026lt;a style=\u0026#34;pointer-events: none;\u0026#34; href=\u0026#39;https://blog.zhoujump.com\u0026#39; target=\u0026#34;_blank\u0026#34;\u0026gt;pointer-events: none\u0026lt;/a\u0026gt; 这个例子中被设置了pointer-events: none的a标签无法被点击，而设置了pointer-events: auto的a标签可以正常点击。我们可以使用这种方式曲线救国实现禁用input输入框的的效果\n完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 \u0026lt;div class=\u0026#34;s-box\u0026#34;\u0026gt; \u0026lt;!-- 老工具人了，不理解它的用法的可以去看一下之前的文章 --\u0026gt; \u0026lt;input id=\u0026#34;s-edit\u0026#34; type=\u0026#34;checkbox\u0026#34;/\u0026gt; \u0026lt;input class=\u0026#34;s-input\u0026#34; value=\u0026#34;点击右侧‘编辑’按钮→\u0026#34;/\u0026gt; \u0026lt;!-- 这个label的内容使用伪元素动态赋予，所以这里留空 --\u0026gt; \u0026lt;label class=\u0026#34;s-label\u0026#34; for=\u0026#34;s-edit\u0026#34;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .s-box {//外部大盒子样式 background: #fff; border: 1px solid #ddd; border-radius: 12px; padding: 10px; transition: all .3s; } .s-box:hover{//外部大盒子被鼠标摸的样式 box-shadow: 0 0 10px #ddd; transition: all .3s; } .s-input,.s-input:focus{//设计input样式和去除它的默认样式 width:calc(100% - 60px); font-size: 16px; padding: 4px; outline: none; border: none; pointer-events: none;//默认情况我们使它无法编辑 border: 1px solid #fff; transition: all .3s; } .s-label{//右侧按钮样式 width: 40px; cursor: pointer; float: right; text-align: center; } #s-edit:checked ~ .s-input{//当点击按钮使checkbox被选中时 pointer-events: auto;//使input可以编辑 border: 1px solid #ddd; border-radius: 4px; transition: all .3s; } #s-edit{display: none;}//隐藏工具人input .s-label::after{content:\u0026#34;编辑\u0026#34;;}//通过伪元素赋予按钮内容 #s-edit:checked ~ .s-label::after{content:\u0026#34;完成\u0026#34;;}//当点击按钮使checkbox被选中时赋予按钮内容 \u0026lt;/style\u0026gt; 相关知识 pointer-events ::after\n","date":"2024-09-29T00:00:00Z","image":"https://blog.zhoujump.com/p/checked_input/cover.webp","permalink":"https://blog.zhoujump.com/p/checked_input/","title":"纯CSS实现输入框的编辑和保存按钮"},{"content":" 文章在个人网站中发布，原文链接：纯CSS实现循环翻牌效果\n先看效果 点击卡片就能切换一张，并且整个实现过程并没有用到js。这个样例中除了两张卡片外其实还有两个透明的input，你点击到的其实是这俩input，而且这俩input会在点击后修改自身的z-index属性，保证你下次点击必定会点击到另外一个。\nz-index 我们对元素使用定位(posation:absolute,fixed,relative)后，元素之间可能会发生重叠，除了html结构中后来居上的原则外，我们还能使用z-index来控制元素的覆盖关系，z-index的值越大，元素越处于顶层。\n下面是一个简短的案例： 我们试试点击这两个巨型单选\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \u0026lt;div\u0026gt; \u0026lt;input name=\u0026#34;demo1\u0026#34; style=\u0026#34;width:100px;height:100px;position:relative\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input checked name=\u0026#34;demo1\u0026#34; style=\u0026#34;width:100px;height:100px;position:relative;right:50px\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; [name=\u0026#34;demo1\u0026#34;]{ z-index: 1;//没被选择的单选框z-index为1 } [name=\u0026#34;demo1\u0026#34;]:checked{//被选择的单选框z-index为2 z-index: 2; } \u0026lt;/style\u0026gt; 被赋予z-index:2的元素会覆盖于z-index:1的元素之上。如果我们将这两个单选框完全重合，然后赋予被选中的单选框较低的z-index,我们就能保证每次被点击的都是未被选中的那个，这就是这个翻牌案例的核心逻辑。\n开干吧 以下是翻牌案例的全部代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- 两个input需要放置在卡片之前但是覆盖在卡片之上，我们使用z-index来实现 --\u0026gt; \u0026lt;input class=\u0026#34;radio1\u0026#34; type=\u0026#34;radio\u0026#34; name=\u0026#34;card\u0026#34;/\u0026gt; \u0026lt;input class=\u0026#34;radio2\u0026#34; type=\u0026#34;radio\u0026#34; name=\u0026#34;card\u0026#34;/\u0026gt; \u0026lt;!-- 两张卡片 --\u0026gt; \u0026lt;div style=\u0026#34;background:#F1948A\u0026#34; class=\u0026#34;card1\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div style=\u0026#34;background:#AED6F1\u0026#34; class=\u0026#34;card2\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; [class*=\u0026#34;card\u0026#34;]{//为卡片设置初始样式 width: 200px; height: 300px; position: absolute; border-radius: 16px; box-shadow: 0 0 10px rgba(0,0,0,0.2); transition: all 1s; } .box{ height: 300px; width: 200px; position: relative; } [class*=\u0026#34;radio\u0026#34;]{//两个单选框宽度高度均与卡片一致，并且完全透明，然后覆盖在卡片之上 position: absolute; width: 200px; height: 300px; z-index: 2; margin: 0; opacity: 0; } .radio1:checked ~ .card1{animation: card .6s;top:0;scale:.98;transition-duration:.6s} .radio1:checked ~ .card2{z-index: 1;top:10px;transition-duration:.6s} .radio1:checked{z-index: 0;}//被选择的单选框设置z-index为0 //这样它被点击后会自己跑到底部去，这样下次点击就会戳到另外一个单选框 .radio2:checked ~ .card2{animation: card .6s;top:0;scale:.98;transition-duration:.6s} //通过后续兄弟选择器选中卡片，这样选中不同的单选框将控制卡片翻一张。 .radio2:checked ~ .card1{z-index: 1;top:10px;transition-duration:.6s} .radio2:checked{z-index: 0;}//与另一个单选框同类 @keyframes card{//这里定义一个翻牌的动画 0%{left:0;z-index:2;top:10px} 50%{left:230px;z-index:2;rotate:10deg;} 51%{left:230px;z-index:0;rotate:10deg;} 100%{left:0;z-index:0;scale:.98;} } \u0026lt;/style\u0026gt; 但是这样似乎只能在两张牌之间翻来翻去，那如何可以增加牌的数量呢。 这时候只需要结合不使用js实现评分组件中的想法，该案例中选择一颗星星就能选中这颗星星之前的所有星星。同理，我们可以实现选中一个单选框就将它之前的单选框z-index值都设为0,选中最后一个单选框后将所有z-index重置，以实现循环翻牌。\n当然这个案例的理论意义是大于实际意义的，每增加一张牌都会产生大量的css代码，其实并不如js来的要好。但是这个案例还是可以作为一个很好的案例，来说明z-index的用法。\n相关知识 z-index;\n@keyframes;\nanimation\n","date":"2024-09-27T00:00:00Z","image":"https://blog.zhoujump.com/p/index-checked/cover.webp","permalink":"https://blog.zhoujump.com/p/index-checked/","title":"纯CSS实现循环翻牌效果"},{"content":" 文章在个人网站中发布，原文链接：随时随地，一键召唤看板娘。\n如何使用 将下面这个蓝色方块拖拽至收藏夹栏再松开鼠标，浏览器会自动创建一个书签。 然后我们随意打开一个网站，点击这个书签，看板娘就会出现在网页上啦！ 这个方法只能用于PC端的浏览器，手机是不可以的。 而且某些网站例如必应，会禁止加载外部资源，这些网站会召唤失败。 百度，B站，思否，掘金都是可以使用的。 加载可能有点慢，需要耐心等待。\n它是如何实现的 浏览器的收藏夹除了可以收藏网址，还可以收藏javascript代码。我们可以通过点击收藏的javascript代码来执行它。\n就像下边这个例子：\n1 2 3 4 5 javascript: alert( \u0026#39;你在看：\u0026#39; + document.getElementsByTagName(\u0026#39;title\u0026#39;)[0].innerText ) 我们复制这些代码然后新建一个书签，将代码粘贴进URL这一栏里然后保存。 然后我们随意打开一个网站，点击这个书签，就会弹出一个对话框，显示当前网页的标题。 不过如果你执行的代码具有返回值，那么就需要在js代码最后添加一行void(0);，否则点击书签将会跳转到另一个页面，在那个页面上显示返回值。\n召唤看板娘的原理 代码如下：\n1 2 3 4 5 6 7 8 javascript: var link = document.createElement(\u0026#39;link\u0026#39;); link.rel = \u0026#39;stylesheet\u0026#39;; link.href = \u0026#39;https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css\u0026#39;; var script = document.createElement(\u0026#39;script\u0026#39;); script.src = \u0026#39;https://fastly.jsdelivr.net/gh/lrplrplrp/bkyl2d@main/loads.js\u0026#39;; document.head.appendChild(link); document.head.appendChild(script); 代码向head标签中添加了两个标签，一个link标签和一个script标签。他们将会加载看板娘所需样式和看板娘的初始化代码，这样看板娘就出现在你的网页上了。\nps:直接拖拽是将一个链接一键添加书签的好方法。\n","date":"2024-09-27T00:00:00Z","image":"https://blog.zhoujump.com/p/creat-waifu/cover.webp","permalink":"https://blog.zhoujump.com/p/creat-waifu/","title":"随时随地，一键召唤看板娘。"},{"content":" 文章在个人网站中发布，原文链接：纯CSS实现评分组件\n先看效果 以下是一个纯靠css实现的评分组件，点击星星即可评分，不同的评分将会展示不同的颜色。\n★ ★ ★ ★ ★ 这个组件是不使用js实现选项选择效果这篇文章的升级版本，一些前置知识其中有提到，感兴趣的同学可以先看看这篇文章。\n后续兄弟选择器~ 不使用js实现选项选择效果这篇文章中我们使用了相邻兄弟选择器+,它可以选择到目标元素的下一个元素。但是上边这个案例中，我们希望点击星星的时候，所有的星星都变成选中状态，这时候就需要用到后继兄弟选择器~了，它可以选择到目标元素之后的所有兄弟元素。\n让我们看看这个简单的案例：\n★ ★ ★ ★ ★ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- 此处我们放了五颗星星 --\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .star{//为星星设置默认样式 color: lightgray; font-size: 24px; } .star:hover ~ .star{//当鼠标移动到星星上时，将会选中后边的所有星星，将其设为金色 color: gold; font-size: 24px; } \u0026lt;/style\u0026gt; 开干把 结合之前的案例，我们加入\u0026lt;input class=\u0026quot;radio\u0026quot;\u0026gt;,再将\u0026lt;span\u0026gt;★\u0026lt;/span\u0026gt;换成\u0026lt;label\u0026gt;★\u0026lt;/label\u0026gt;，似乎胜利就在眼前了。\n评分组件案例的全部代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- 我们先创建五个input，它们必须摆在星星前边，方便使用后续兄弟选择器选中星星 --\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star1\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star2\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star3\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star4\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star5\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;!-- 再创建五个星星并且与上边五个input使用for绑定 --\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star1\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star2\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star3\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star4\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star5\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{//使用弹性布局让星星横向排列 position: relative; display: flex; } .radio{//工具人input还是隐藏掉 display: none; } .label{//为星星设置默认样式 color: #ccc; transition-duration:.3s; font-size: 24px; scale:.9; } #star1:checked ~ .label:nth-child(6), #star2:checked ~ .label:nth-child(-n+7) {//当#star1被用户选中时，找到后边的所有.label //再使用孩子选择器选中第6个元素(前五个是input)，为其设置颜色 color:#515A5A; transition-duration:.3s; scale: 1; } #star3:checked ~ .label:nth-child(-n+8), #star4:checked ~ .label:nth-child(-n+9) {//如法泡制，第三四颗星星开始设置蓝色 color:#3498db; transition-duration:.3s; scale: 1; } #star5:checked ~ .label {//最后一颗星星就不需要孩子选择器了，直接选中所有星星设置金色 color:#f1c40f; transition-duration:.3s; scale: 1; } \u0026lt;/style\u0026gt; 于是我们就完成了这个不需要js的评分组件，甚至能够不使用js正常提交评分，是不是很简单呢？\n","date":"2024-09-25T00:00:00Z","image":"https://blog.zhoujump.com/p/input-star/cover.webp","permalink":"https://blog.zhoujump.com/p/input-star/","title":"纯CSS实现评分组件"},{"content":" 文章在个人网站中发布，原文链接：纯CSS实现选项选择效果\n先看效果 你可以通过点击来切换不同的选项，而这个样例没有使用js而是完全使用css实现的。\n选项1 选项2 选项3 选项4 :checked伪类选择器 实现这个效果的核心是:checked伪类选择器。这个选择器可以匹配被用户点击的元素，也就是说我们能为用户选择的元素单独设置样式。\n废话不多说，直接上代码。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- 首先创建input元素，虽然它不会显示，但是我们需要它才能使元素可以被选择 --\u0026gt; \u0026lt;input checked type=\u0026#34;radio\u0026#34; id=\u0026#34;radio1\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;!-- 然后在input后紧接着一个lable，通过for属性与上边的input绑定，这样点击这个lable就等价于点击了input --\u0026gt; \u0026lt;label for=\u0026#34;radio1\u0026#34;\u0026gt;选项1\u0026lt;/label\u0026gt; \u0026lt;!-- 如法炮制几个选项 --\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio2\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio2\u0026#34;\u0026gt;选项2\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio3\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio3\u0026#34;\u0026gt;选项3\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio4\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio4\u0026#34;\u0026gt;选项4\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{ display: flex;//为容器设置弹性布局使其内部元素横向排布。 line-height: 120px; text-align: center; } label{//这里就是lable的样式啦。 margin-right: 10px; width: 60px; height: 120px; background: #ccc; border-radius: 10px; transition-duration:.3s; } input{ display: none;//隐藏input元素，它只是工具人而已。 } input:checked + label{//使用checked找到被用户点击的input，再通过+相邻兄弟选择器选择到这个input后紧挨着的lable，为它设置样式。 transition-duration:.3s; width: 120px; background:#99e6ff; color:#006080; } \u0026lt;/style\u0026gt; 在上边的代码中我们利用了\u0026lt;input type=\u0026quot;radio\u0026quot;\u0026gt;可以被用户点击的特性，然后再将lable元素与其绑定并放置在其后边。这样不仅可以通过点击lable来选中input，还能使用相邻兄弟选择器通过 input来选中lable。 此时作为工具人的input元素就能从页面中隐藏了，我们只需要为可操作性更强的lable元素设计样式即可。 使用这种技巧能实现的效果能远超你的想象。\n相关知识 input的radio类型;\n:checked伪类选择器;\n相邻兄弟选择器;\nlable及其for属性\n","date":"2024-09-25T00:00:00Z","image":"https://blog.zhoujump.com/p/checked-css/cover.webp","permalink":"https://blog.zhoujump.com/p/checked-css/","title":"纯CSS实现选项选择效果"},{"content":" 文章在个人网站中发布，原文链接：如何实现一个宽度随文字变化的输入框\n有些时候我们需要制作宽度随输入的文字变化的输入组件，例如下面这个tag输入框：\n我的内容是可以编辑的 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;div style=\u0026#34;height: 30px; background:#99e6ff; color:#006080; padding:0 8px; border-radius:5px; display:inline-block; line-height:30px; outline: none; margin-bottom: 16px;\u0026#34; contenteditable=\u0026#34;true\u0026#34;\u0026gt;我的内容是可以编辑的\u0026lt;/div\u0026gt; 这个时候常规的input就显得有些困难了,因为input的宽度是固定的。\n根据上面的代码，我们可以发现这种效果是通过一个不能输入的元素实现的，秘密就在style中的最后一个属性contenteditable=\u0026quot;true\u0026quot;，这个属性可以让元素变成一个可编辑的文本框，但是它不会显示出来，所以看起来就像是一个普通的div一样。\n合理使用这个属性，我们可以实现很多有趣的效果。使我们的开发效率事半功倍。\n","date":"2024-09-24T00:00:00Z","image":"https://blog.zhoujump.com/p/contenteditable-input/cover.webp","permalink":"https://blog.zhoujump.com/p/contenteditable-input/","title":"如何实现一个宽度随文字变化的输入框"},{"content":" 文章在个人网站中发布，原文链接：为不确定高度的元素添加过渡动画\n有时候我们会遇到这种情况，容器内部由元素撑开无法知道其高度，又需要实现平滑过渡。就像这样：\n把鼠标摸我头上试试 我是内容 我是内容 我是内容 我是内容 我相信作为CSS初学者都有过这样的尝试：\n1 2 3 4 5 6 7 8 .cont{ height:0;//初始状态时高度为零 transition: .3s; } .cont:hover{ height:auto;//鼠标摸上去时高度为自动 transition: .3s;//设置0.3秒过渡 } 然后狠狠吃了一个闭门羹，鼠标摸上去确实会变换高度，但过渡不生效。这是因为transition只能处理数值之间的过渡，auto - 0之间的变化无法过渡。\n现在通过calc-size属性能够实现这种过渡了，具体请看这篇帖子,不过目前兼容性还不是很好。\n那有什么办法能做到呢，此时就要搬出我们的display: grid了，网格布局可以通过fr单位控制自己内部容器的宽高比例。那就很巧了家人们，只需要对高度比例作1 - 0之间的变换不就成了吗？\n下面是上文中摸头展开样例的代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;head\u0026#34;\u0026gt;我来组成头部\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;body\u0026#34;\u0026gt; 我是内容\u0026lt;/br\u0026gt; 我是内容\u0026lt;/br\u0026gt; 我是内容\u0026lt;/br\u0026gt; 我是内容\u0026lt;/br\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{ display:grid;//设置网格布局 background:#99e6ff; color:#006080; grid-template-rows: 30px 0fr;//分配高度：head:30px，body:0% border-radius:8px; transition: .3s; padding:10px; line-height:30px; } .cont:hover{ grid-template-rows: 30px 1fr;//分配高度：head:30px，body:100% transition: .3s;//设置0.3秒过渡 } .body{ min-height: 0;//设置最小高度，不然文字仍能把容器撑开。 overflow:hidden;//设置超出隐藏，不然容器收起来了，文字还在。 } \u0026lt;/style\u0026gt; 水灵灵的就完成了一个效果，有关网格布局的知识三言两语无法讲明白，有兴趣的同学可以学习学习。\n本文参考 CSS 如何让auto height完美支持过渡动画？\n","date":"2024-09-24T00:00:00Z","image":"https://blog.zhoujump.com/p/grid-transition/cover.webp","permalink":"https://blog.zhoujump.com/p/grid-transition/","title":"为不确定高度的元素添加过渡动画"},{"content":" 文章在个人网站中发布，原文链接：HUGO，评论！\n前言：本文继续上文，讲一讲如何为你的博客添加评论系统。 阅读之前的文章\n部署waline 这部分waline官网有详细介绍，我们只需要做到 Vercel 部署 (服务端) 这一步即可。有购买域名的小伙伴可以往下再做一步。完成部署后就能回到本文继续配置啦。 点击前往waline教程\nvercel如果出现访问困难，请使用魔法上网。\n部署hugo 我们假设你完全按照教程配置了hugo，而且没有使用其它的hugo主题。\n进入已经部署的vercel项目 选择Settings\u0026gt;Domains前往域名管理界面，复制一个域名备用。\n前往gitlab，找到config/_default/params.toml文件，编辑从107行开始的代码\n1 2 3 4 5 6 7 8 9 [comments.waline] serverURL = \u0026#34;domain.com\u0026#34;//此处填写你在上一步中复制的域名 lang = \u0026#34;zh-CN\u0026#34; visitor = \u0026#34;\u0026#34; avatar = \u0026#34;\u0026#34; emoji = [\u0026#34;https://unpkg.com/@waline/emojis@1.1.0/weibo\u0026#34;] meta = [\u0026#39;nick\u0026#39;, \u0026#39;mail\u0026#39;]//填写你想让用户留下的信息 requiredMeta = [\u0026#39;nick\u0026#39;]//填写用户必填信息，为空则允许匿名 placeholder = \u0026#34;留下你的评论吧！\u0026#34; 然后是77行的代码\n1 2 3 4 ## Comments [comments] enabled = true provider = \u0026#34;waline\u0026#34; 有关此处配置的详细信息可以点击这里详细了解\n编辑完，然后保存。等待流水线运行，之后你就能看见在文章末尾出现评论区了。\n","date":"2024-09-19T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-commits/cover.webp","permalink":"https://blog.zhoujump.com/p/hugo-commits/","title":"HUGO，评论！"},{"content":" 文章在个人网站中发布，原文链接：HUGO，域名！\n前言：本文继续上文，讲一讲如何购买一个域名，然后将咱的网页绑定在其上。 阅读之前的文章\n购买域名 查询域名 在购买域名之前，第一步应该是查询想购买的域名是否还在。就像你没法购买baidu.com，因为这个域名目前是归百度的，你只能购买那些没被注册的域名(当然没有什么是钞能力没法办到的)。\n提供域名注册的厂商很多，你可以百度一下‘域名注册’，会有很多的结果。本文以腾讯云为例注册域名。点击我跳转至腾讯云域名注册。\n现在的域名购买流程非常简单，搜索感兴趣的域名，系统会给出推荐。顺带一提，现在中文域名的支持也已经比较好了。\n我们选择一个域名加入购物车，然后购买它，当然第一次使用腾讯云的话需要走一遍注册和实名认证的流程。一般来说注册第一年会给一个比较优惠的价格。\n然后前往域名控制台就能看到你购买的域名了。\n绑定至pages gitlab内设置 再次回到 部署\u0026gt;pages 点击新域名按钮\n输入一个你所购买的域名的子域名,然后点击创建新域名\n我购买了zhoujump.com，它被称为一个根域名。例如www.zhoujump.com,blog.zhoujump.com这样的域名就是子域名，你可以随便取名。当然你也可以使用根域名，但是可能会在下一步遇到麻烦。\n如果你是第一次添加,验证状态这里可能是红色的，没关系，我们马上让他变成绿色。\n域名设置 上一步的页面我们先放到一边，前往域名控制台，找到你购买的域名，选择解析。\n然后新建两条解析记录，按照之前那个页面给出的信息填写完整。并保存。\n此时我们静待几分钟，因为解析生效需要一点时间，然后回到之前那个页面点击刷新按钮，你会发现验证状态变绿了。此时我们就能保存更改了\n完成这些设置后，我们把刚刚绑定的子域名输入浏览器地址栏然后回车。你的网站已经可以使用域名访问了\n","date":"2024-09-17T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-domain/cover.webp","permalink":"https://blog.zhoujump.com/p/hugo-domain/","title":"HUGO，域名！"},{"content":" 文章在个人网站中发布，原文链接：HUGO，启动！\n前言：本文章将光速教会你如何启动一个hugo项目，并且将其免费托管至gitlab pages以供所有人访问，就像你现在看到的这个网站一样。\n准备账号 目前你需要准备的非常简单，只需要一个gitlab账号，没有的话可以点击这里去注册一个。\n如果遇到这种情况，你可能需要魔法上网。大陆特供版极狐不提供pages服务\n注册完成后会要求你填写一些信息，当你填写到这一步时就能进行部署了。\n拉取部署 创建项目 群组名称请取一个你喜欢的名字，然后点击下边的仓库(URL) 按钮。\n接下来往下翻，在Git仓库URL中填写https://github.com/CaiJimmy/hugo-theme-stack-starter.git\n项目名称和项目标识串也是取一个你喜欢的名字，然后可见性级别选择‘公开’，\n最后点击新建项目\n启动项目 如图，点击 + 再点击新建文件，让我们来新建一个配置文件以启动gitlab流水线。\n文件名处填写.gitlab-ci.yml。 正文输入以下内容：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 variables: DART_SASS_VERSION: 1.77.5 HUGO_VERSION: 0.128.0 NODE_VERSION: 20.x GIT_DEPTH: 0 GIT_STRATEGY: clone GIT_SUBMODULE_STRATEGY: recursive TZ: America/Los_Angeles image: name: golang:1.22.1-bookworm pages: script: # Install brotli - apt-get update - apt-get install -y brotli # Install Dart Sass - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - cp -r dart-sass/ /usr/local/bin - rm -rf dart-sass* - export PATH=/usr/local/bin/dart-sass:$PATH # Install Hugo - curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb - apt-get install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb - rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb # Install Node.js - curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash - - apt-get install -y nodejs # Install Node.js dependencies - \u0026#34;[[ -f package-lock.json || -f npm-shrinkwrap.json ]] \u0026amp;\u0026amp; npm ci || true\u0026#34; # Build - hugo --gc --minify # Compress - find public -type f -regex \u0026#39;.*\\.\\(css\\|html\\|js\\|txt\\|xml\\)$\u0026#39; -exec gzip -f -k {} \\; - find public -type f -regex \u0026#39;.*\\.\\(css\\|html\\|js\\|txt\\|xml\\)$\u0026#39; -exec brotli -f -k {} \\; artifacts: paths: - public rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 然后往下翻，选择提交更改。\n接下来我们点击构建，再点击流水线，就能看到目前正在构建项目。\n如果你是现在才注册的gitlab账号，可能会要求你验证电话号码，你需要准备一个非大陆的电话号码用于接收短信。\n当流水线状态变为已通过后，我们点击部署，再点击Pages，就能看到gitlab为我们分配的网址了。\n我们点击这个链接，就能打开咱的网站了。\n如果打开后界面显示异常，需要前往config/_default/config.toml文件修改部分配置，将baseurl中的内容改为上一步中gitlab分配的地址。保存文件后再次等待流水线执行，然后刷新页面，网站应该就可以正常显示了。\n此处因为我绑定了域名，所以填写了域名，下一篇文章我将教你如何购买和绑定域名。\n","date":"2024-09-14T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-start/cover.webp","permalink":"https://blog.zhoujump.com/p/hugo-start/","title":"HUGO，启动！"}]