Vue+TailwindCSS实现输入框动效

实现目标: 静态属性:输入框位于页面顶端、居中,圆角,框体内部为白色,1/5屏幕宽度,无外边框、框体阴影,闪烁光标为蓝色 动态属性:鼠标悬停时框体微微变大;聚焦后出现蓝色外边框,宽度增长且仍保持居中,框体阴影变为蓝色 其他:聚焦输入框时,背景逐渐模糊,取消聚焦后恢复 静态设计 HTML中的<input>标签可以承担基本的输入功能,创建文件SearchItem.vue,接下来使用TailwindCSS对默认输入框标签的样式进行调整 对于输入框位置与默认宽度的要求,可以使用网格布局,每行5列,而输入框位于第三列,这一布局设定应当在上层的文件中,而非SearchItem文件,所以这里先不对其进行设置 圆角、无外边框、框体阴影和蓝色光标可以使用rounded-xl、outline-none、shadow-xl、caret-blue-500来实现 考虑到后续会添加样式切换的动效,需要再加上transition-all以保证样式更改过程是平滑的 动态设计 TailwindCSS中,鼠标悬停的样式只需要使用hover:即可;获取焦点时的样式只需要使用focus: 悬停时框体变大:hover:scale-105 为了使悬停 -> 聚焦之后框体大小保持稳定,同时设置focus:scale-105 聚焦后出现蓝色外边框:focus:border-2、border-blue-500 宽度增长且仍居中,可以将元素左移的同时将宽度增加与之相同的长度:focus:-translate-x-40、focus:w-[40rem] 框体阴影变为蓝色:focus:shadow-blue-300/50 另外,可以设置转化的时长,这里设置400ms:duration-[400ms] 至此,对输入框的样式设计完成: <template> <input placeholder="搜索" class="shadow-xl duration-[400ms] hover:scale-105 focus:-translate-x-40 focus:w-[40rem] focus:border-2 focus:shadow-blue-300/50 focus:scale-105 border-blue-500 px-5 py-3 rounded-xl w-full transition-all outline-none caret-blue-500" name="search" type="search" /> </template> 上层结构 在上层文件中引入SearchItem.vue,然后设置网格布局并使这一模板位于5列中的第三列: <script setup lang="ts"> import searchItem from "./components/SearchItem.vue"; </script> <template> <div> <div class="grid grid-cols-5 gap-4 mt-2"> <searchItem class="col-start-3" /> </div> </div> </template> 效果如下: 为了使输入框能够输入内容,需要将输入的文字绑定至Vue中: <script setup lang="ts"> import { ref } from "vue"; defineOptions({ name: "searchItem", }); const input = ref(""); </script> <template> <input placeholder="搜索" class="shadow-xl duration-[400ms] hover:scale-105 focus:-translate-x-40 focus:w-[40rem] focus:border-2 focus:shadow-blue-300/50 focus:scale-105 border-blue-500 px-5 py-3 rounded-xl w-full transition-all outline-none caret-blue-500" name="search" type="search" v-model="input" /> </template> 背景模糊 接下来添加背景模糊 ...

December 19, 2024

基于归并排序的逆序数统计

问题定义 给定一个长度为n的整数数组,计算数组中的逆序对的数量并返回 逆序对的定义如下:对于数组的第i个和第j个元素,如果满足i<j且a[i]>a[j],则其为一个逆序对;否则不是。 数据范围 1≤n≤100000,数组中的元素的取值范围 [1,1e9]。 示例:输入[2,3,4,5,6,1],输出5 解释:逆序对共5对,[2, 1], [3, 1], [4, 1], [5, 1], [6, 1] 本题暴力硬解的复杂度为$O(n^2)$,较为繁琐,于是考虑是否存在复杂度为$O(nlogn)$的算法 要求逆序数,就不能在寻找数对时未经相关处理就更改数组元素顺序打乱原有顺序,因此要找到一种在实施过程中能够在一定程度上保留元素顺序关系的算法。 设想一种很理想的情况:元素a与b满足逆序数的定义(即a>b且i<j),同时还已知有一批比a大的数,那么很容易推知这一批数都可以与b构成逆序数,其个数就等于这批数的个数 以上情况正好符合归并排序的过程,首先在这里复习一下归并排序的原理: 归并排序 归并排序先递归地两两划分数组,每轮递归中都将整个数组分为左右两个部分,然后在每一次左右划分完成后,为左右两个数组各设置一个指针i与j,比较大小并将小者放入一个临时数组中(这个临时数组的长度是本轮划分左右数组的总长),放入一边的指针前进一步,再继续比较。当某一边全部比较完后,将可能还未放完的另一边数组元素全部放入临时数组 代码可以是: class Solution { public int[] mergeSort(int[] nums, int l, int r) { // l=r证明已经划分到单个元素,返回的排序数组就是以这个单元素组成的数组 if (l == r) return new int[]{nums[l]}; int mid = (l + r) / 2; int[] leftNums = mergeSort(nums, l, mid); int[] rightNums = mergeSort(nums, mid+1, r); // 能执行到这里,说明左右划分都已完成,此时如果左右都是单元素数组[2][3],则这个栈会返回[2,3] // 此时如果左右都是多元素数组,那至少能保证这两个数组各自的内部是有序的 int[] tempNums = new int[leftNums.length + rightNums.length]; int i = 0, j = 0, m = 0; // i左j右,m是临时数组索引 while (i < leftNums.length && j < rightNums.length) tempNums[m++] = leftNums[i] < rightNums[j] ? leftNums[i++] : rightNums[j++]; // 接下来处理一边走完一边还没走完的情况,由于单边必定已经有序,所以直接顺序放入即可 while (i < leftNums.length) tempNums[m++] = leftNums[i++]; while (j < rightNums.length) tempNums[m++] = rightNums[j++]; return tempNums; } } 题解 回到本题,核心在于:在归并排序比较leftNums[i]与rightNums[j]的大小关系时,如果正好左大于右,(即满足逆序数的定义),则可以直接确定这两个数之间的数都满足逆序数的定义 ...

December 11, 2024

顺序统计

顺序统计的问题是: 给定一组元素,找到其中第k大(小)的元素 仅从功能实现上考虑,完全可以先对数组排序,然后直接取nums[k-1]作为结果输出。但这个方法是需要先对数组排序的,所以效率为$\Theta(nlogn)$,我们的目标是找到一种$\Theta(n)$的方法 基于随机数的分治法 为了尽量压缩时间,可以考虑能不能在排序进行时就顺便找到所需元素,为此,可以使用快速排序算法中的分治思想 在快速排序算法中,每选定一个基准数pivot,就需要将所有小于pivot的元素移动至其左侧、所有大于pivot的元素移动至其右侧,当全部移动完成时,左右指针会正好相遇在pivot元素上 不难想到,当获得了pivot的索引值index,就可以直接检查index是否正好等于k-1(假设k指第k小的元素值),如果相等,则说明这个pivot就是要找的第k小的元素;如果不等,则进一步检查index与k-1之间的大小关系并确定新pivot: 如果index > k-1,说明要寻找的元素相较于pivot来说更小,那么去小于pivot的区域再找 如果index < k-1,说明要寻找的元素相较于pivot来说更大,那么去大于pivot的区域再找 以上过程完全可以用递归来实现(事实上这也是快速排序所进行的过程),而在区域中选取pivot实际上是随机取得的。另外,当传入的左右限制left == right时,说明此区域仅有一个元素,需要寻找的只能是该元素,直接返回nums[left]即可 代码实现(Java) 类的总体结构应为: import java.util.Random; class Find { private int[] nums; private int k; public Find(int[] nums) {...} public int getKElement(int k) {...} private int randomSelect(int left, int right) {...} // 递归部分,选取随机数、划分后判断 private int partition(int random, int p, int q) {...} // 划分部分 private void swap(int i, int j) {...} // 用于交换数组元素的工具方法 } 先编写划分部分的方法,输入数组、随机索引值、左右限制,输出划分后pivot的索引值: public int partition(int random, int p, int q) { while (p != q) { int pivot = nums[random]; while (pivot > nums[p]) p++; swap(nums, p, random); random = p; while (pivot < nums[q]) q--; swap(nums, q, random); random = q; } return random; } 编写递归部分: ...

December 10, 2024

Stream流遍历处理字符串中各字符

Stream在数组处理上的特点 首先明确一点: char[] chs = {'h','e','l','l','o'}; int[] ints = {1,2,3,4}; String str = "hello"; String[] strs = {"hello","world"}; //数组的Stream流泛型都是数组,而不是数组中的单个元素 Stream<char[]> chsStream = Stream.of(chs); Stream<int[]> intsStream = Stream.of(ints); //String的Stream流泛型都是String Stream<String> strStream = Stream.of(str); Stream<String> strsStream = Stream.of(strs); //在这种情况下,流中的每个元素都是String[]数组中的单个元素 如果我们的目的是处理数组中的每一个元素,那么对于Stream<T[]>这种类型的流我们一般是并不想见到的 解决方法就是使用基本类型流,即IntStream、DoubleStream和LongStream,然后再操作(或对基本类型流先进行boxed()转为Stream<T>再操作),而得到基本类型流的方法有两种: Arrays.stream() 基本类型流名.of() Stream处理字符串 - 以凯撒密码为例 若要对字符串中的每个字符进行逐个操作,最容易想到,也最快的方法就是先使用toCharArray()将字符串转为一个char[]数组,再使用循环进行操作,最后使用String的构造方法将之再转换回字符串: //正常方法 char[] chars = str.toCharArray(); for(int i=0;i< chars.length;i++) { chars[i] = (char)((chars[i]-'a'+k)%26+'a'); } System.out.println(new String(chars)); 这次我舍近求远,想用Stream流来模拟生成凯撒密码密文的操作 根据Stream处理数组的特点,String转换成char[]之后,要想对数组的每个元素都进行操作,就要使用基本类型流,但并没有char类型的基本类型流,在这就导致我只能得到Stream<char[]>,而得不到如CharStream或是Stream<Character>的流 所以,不能将String转为char[]数组 那么,借助String[]数组的流中可以遍历到数组中的每个元素的特点,考虑将String转为String[]数组,再使用map()将每个元素转换成char,这样才得到一个Stream<Character>: Stream<String> t = Stream.of(str.split("")); //str:{"h","e","l","l","o"} Stream<Character> chStream = t.map(s -> s.charAt(0)); chStream = t.map(ch -> (char)((ch-'a'+1)%26+'a'); /* * 也可直接一步完成操作: * s -> (char)((s.charAt(0)-'a'+1)%26+'a') */ 得到了Stream<Character>以后,才能正式地开始遍历每个字符并处理。处理结束后,将Stream<Character>重新拼接成字符串即可 ...

September 6, 2022

Stream流进行数组排序

考虑一个数组: int[] nums = {9,6,5,7,4,8,3,1,2}; 对于数组,列举几个转换Stream流的操作及返回值: //返回Stream对象,但泛型为int[]数组 Stream<int[]> nums1 = Stream.of(nums); //返回一个IntStream对象,默认无泛型 IntStream nums2 = IntStream.of(nums); IntStream nums3 = Arrays.stream(nums); 若想要对数组进行排序,则使用sorted()方法,但需要注意的是,IntStream的sorted无入参,即只能自然排序,只有Stream中的sorted才能指定比较器,所以将之转化为Stream类型,再进行排序: //使用boxed()将IntStream转换为Stream类型,即将IntStream中的每个整型都进行装箱 //nums2同理 Stream<Integer> boxedNums = nums3.boxed(); //进行排序 Stream<Integer> sortedNums = boxedNums.sorted((o1,o2) -> o2-o1); 排序完成后,仍是一个Stream对象。若想将之转换回数组,则使用toArray()方法 但仍然需要注意,在Stream中,由于Stream的泛用性,toArray()返回的是Object类型的数组,而非int类型,所以,需要首先转化为IntStream,表示其中存储的都是整型数据,然后使用该对象中的toArray()方法: //使用mapToInt转化为IntStream对象 //此处的intValue是将原本的Integer包装类转换为int基本类 IntStream temp = sortedNums.mapToInt(Integer::intValue); //最终转换为数组 int[] res = temp.toArray(); 以下总结前文提到的Stream和IntStream的同名方法及必要说明,方便判断是否需要进行对象类型的转换: Stream: Stream<T> of(T t):返回一个Stream对象,其泛型是参数泛型 Stream sorted():可带参可不带参 Object[] toArray():返回一个Obj的数组 IntStream: IntStream of(int… values):返回一个IntStream对象,直接存有数组每个元素 IntStream sorted():只有无参的 int[] toArray():返回一个int的数组 此外,Arrays.stream()也能返回一个IntStream对象,效果与IntStream.of()一致,且其针对数据数组有更多重载,泛用性更强

September 5, 2022