使用 Vue3 实现锚点组件

news2024/11/19 23:36:51

目录

1. 需求介绍

2. 实现过程

2.1 表单结构介绍

2.2 确定锚点组件接收的参数及使用方法

2.2.1 form-dom:需要被锚点组件控制的表单实例

2.2.2 active-anchor:默认激活的锚点

2.2.3 title-class:表单标题特有的类名

2.2.4 将 锚点组件 挂载到 body 上

2.2.5 锚点组件使用示例

2.3 实现锚点组件基本结构

2.4 锚点组件 onMounted() 时,要执行的操作

2.4.1 从表单实例中,获取锚点列表 getAnchorList()

2.4.2 激活默认锚点,滚动到指定位置

2.4.3 添加滚动事件监听

2.4.4 给滚动事件添加防抖

2.5 滚动事件实现逻辑

2.5.1 阻止事件向上传播

2.5.2 根据表单已经滚动的高度,判断激活哪个锚点

2.6 添加锚点项点击事件

2.7 实现返回顶部按钮功能

2.8 最终代码


1. 需求介绍

 

如图所示,锚点组件实现了以下功能:

  • 锚点组件显示表单所有的标题
  • 锚点组件存在 “返回顶部” 钮
  • 当表单滚动时,锚点组件对应目录自动高亮
  • 点击锚点组件列表项时,表单滚顶到指定章节处
  • 当前激活的锚点索引应该高亮

2. 实现过程

2.1 表单结构介绍

此项目表单需要每个模块可以折叠,所以采用 ElementPlus 中的折叠面板,如下所示:

  • 使用 div 包裹所有表单内容,并定义 ref="useAnchorFormRef" 用于获取表单实例
  • 使用 el-collapse-item 包裹每一项表单内容
  • 使用 .details-container__submenu 包裹标题,该类名后面会作为锚点内部寻找标题的依据
<div ref="useAnchorFormRef" class="details-container--scroll">
  <el-collapse v-model="activeNames" @change="handleCollapseChange">
    <!-- 任务审核意见 -->
    <el-collapse-item
      v-if="type === TaskViewPageTypeEnum.check"
      :title="TaskViewCollapseNameEnum.taskReviewComments"
      :name="TaskViewCollapseNameEnum.taskReviewComments"
    >
      <!-- 标题 -->
      <template #title>
        <div class="details-container__submenu">
          {{ TaskViewCollapseNameEnum.taskReviewComments }}
        </div>
      </template>
      <!-- 多行文本 -->
      <el-input v-model="reviewComments" type="textarea" :disabled="true" :rows="4"></el-input>
    </el-collapse-item>
  </el-collapse>
</div>

2.2 确定锚点组件接收的参数及使用方法

2.2.1 form-dom:需要被锚点组件控制的表单实例

为了让表单页面中的逻辑尽量精简,只关心表单业务本身;与业务无关的逻辑(关于表单滚动监听的事件),都考虑在锚点组件中实现,因此锚点组件需要接收表单组件实例;

2.2.2 active-anchor:默认激活的锚点

有些表单,要求一进来就定位到指定的模块,激活指定的锚点

2.2.3 title-class:表单标题特有的类名

用于判断元素的 offsetTop,此处使用 .details-container__submenu 作为标题类名,可以自己定义;简单来说,我需要获取每个标题距离可视区域顶部的范围,通过类名,获取表单标题 DOM实例,进而获取 DOM 实例的 scrollTop 属性实现

综上所述,最终接收的 props 长这个样子:

props: {
  // 使用锚点的表单实例
  formDom: {
    type: Object,
    default: () => ({}),
    required: true,
  },
  // 默认激活哪个锚点
  activeAnchor: {
    type: Number,
    default: 0,
  },
  // 章节特有的类名
  titleClass: {
    type: String,
    default: '.details-container__submenu',
  },
},

 

2.2.4 将 锚点组件 挂载到 body 上

锚点组件涉及到了定位,如果直接挂载到元素内部,会被父元素的 position 影响到,而导致定位位置不可控因素变多,因此使用 teleport 将他挂载到 body 上,确保位置固定

由于锚点列表依据于表单数据,因此需要在表单实例加载完成后,才能渲染锚点组件

2.2.5 锚点组件使用示例

<!-- 锚点组件 -->
<teleport :disabled="false" to="body">
  <!-- form-dom:需要被锚点组件控制的表单实例 -->
  <!-- active-anchor:默认激活的锚点,设置此项后,进入表单会自动定位锚点,并滚动到相应位置 -->
  <!-- v-if="useAnchorFormRef" 此判断必须存在,防止传入 表单实例DOM 为空的问题 -->
  <anchor-point v-if="useAnchorFormRef" :form-dom="useAnchorFormRef" :active-anchor="0"></anchor-point>
</teleport>

2.3 实现锚点组件基本结构

如下所示,除了需要展示锚点列表,还需要展示 返回顶部 的按钮

<div class="anchor__container">
  <template v-if="anchorList.length">

    <div
      v-for="node in anchorList"
      :key="node.index"
      class="anchor__item"
      :label="node.label"
      :class="{ active: currentAnchor === node.index }"
      @click="handleAnchorClick(node)"
    >
      {{ node.label }}
    </div>
  </template>

  <div class="anchor__return-top" @click="handleReturnTop">
    返回顶部
  </div>
</div>

2.4 锚点组件 onMounted() 时,要执行的操作

2.4.1 从表单实例中,获取锚点列表 getAnchorList()

先定义三个变量:

  • 锚点列表
  • 当前激活的锚点索引
  • 表单实例中,标题的 DOM 实例列表

响应式变量如下所示:

// 响应式变量
const state = reactive({
  // 锚点列表
  anchorList: [] as any[],
  // 当前激活的锚点索引
  currentAnchor: 0,
  // 表单实例中,章节 DOM 列表(锚点列表的内容就是通过这个变量填充的)
  titleListInForm: [] as any[],
});

 

接下来要执行这些操作:

  • 清空锚点列表
  • 根据 props.title-class 以及 querySelectorAll() 获取全部表单标题 DOM 实例,为了让 DOM 实例列表变成数组,使用 Array.form 处理他们
  • 遍历标题 DOM 列表,填充锚点列表;需要注意:要实现点击锚点,滚动到表单指定区域,就要在每一项锚点数据中,填充上当前锚点需要让表单滚动多大距离,也就是此处的 top;
/**
 * 从表单实例中,获取章节列表,并填充锚点列表
 */
const getAnchorList = () => {
  // 清空锚点列表
  state.anchorList = [];

  // 获取表单实例中的章节 DOM 列表
  state.titleListInForm = Array.from(props.formDom.querySelectorAll(props.titleClass));
  // console.log('获取表单实例中的章节 DOM 列表 titleListInForm ===', titleListInForm);

  // 遍历章节 DOM 列表,填充锚点列表
  state.titleListInForm.forEach((item: any, index) => {
    // console.log('当前遍历的 章节 DOM item ===', item);
    state.anchorList.push({
      index, // 章节索引
      label: item.innerHTML || '--', // 章节内容
      top: item.offsetTop,
      titleDOM: item, // 章节完整 DOM 信息
    });
  });
  // console.log('填充锚点列表 state.anchorList ===', state.anchorList);
};

2.4.2 激活默认锚点,滚动到指定位置

实现思路:

  • 如果不是默认激活第一项,则要手动激活锚点项,并滚动到指定位置
  • 遍历锚点列表,寻找和当前表单所处位置(第几个)一致的锚点索引,将该锚点对应的标题组件存到临时变量中
  • 如果找到了对应的标题 DOM,则使用 el.scrollIntoView() 方法,平滑的滚动到对应位置

注意:此处应该使用定时器,否则会导致滚动不生效

// 如果默认激活的锚点,不是第一个,则要先进行一次滚动
if (props.activeAnchor !== 0) {
  state.currentAnchor = props.activeAnchor;
  // 即将滚动到的目标章节 DOM
  let showTitleDomStart: any;
  state.anchorList.forEach((item: any) => {
    const indexTemp = item.index;
    if (props.activeAnchor === indexTemp) {
      showTitleDomStart = item.titleDOM;
      console.log('默认滚动到的 章节DOM', item.titleDOM);
    }
  });
  // 如果找到了符合条件的章节 DOM
  if (showTitleDomStart) {
    setTimeout(() => {
      // 平滑滚动
      showTitleDomStart.scrollIntoView({
        behavior: 'smooth',
        block: 'start',
      });
    }, 500);
  }
}

2.4.3 添加滚动事件监听

这里需要注意:props 传进来的 表单 DOM 实例,可以直接使用,不要添加 .value

挂载时,需要添加滚动事件监听,卸载时,要记得取消滚动事件监听

onMounted(() => {
  // 给表单添加滚动监听
  props.formDom.addEventListener('scroll', handleDebounceScroll);
});

onUnmounted(() => {
  // 移除表单滚动监听
  props.formDom.removeEventListener('scroll', handleDebounceScroll);
});

2.4.4 给滚动事件添加防抖

只要页面发生变化,就会触发滚动事件;因此,一定要添加防抖事件,避免影响性能

/**
 * 防抖 在事件被触发一定时间后再执行回调,如果在这段事件内又被触发,则重新计时
 * 使用场景:
 * 1、搜索框中,用户在不断输入值时,用防抖来节约请求资源
 * 2、点击按钮时,用户误点击多次,用防抖来让其只触发一次
 * 3、window 触发 resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
 * @param fn 回调
 * @param duration 时间间隔的阈值(单位:ms) 默认1000ms
 */
export function useDebounce<F extends(...args: unknown[]) => unknown> (fn: F, duration = 1000):
() => void {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

  const debounce = (...args: Parameters<F>) => {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    timeoutId = setTimeout(() => {
      fn(...args);
      timeoutId = undefined;
    }, duration);
  };

  return debounce;
}

/**
 * 对滚动事件进行防抖处理,节约性能
 */
const handleDebounceScroll = useDebounce(handleScroll, 200);

2.5 滚动事件实现逻辑

2.5.1 阻止事件向上传播

/**
 * 处理滚动事件
 */
const handleScroll = (e: any) => {
  // console.log('处理滚动事件', e);
  e.stopPropagation();
  // 根据表单已经滚动的高度,判断激活哪个锚点
  activeFixedAnchor();
};

2.5.2 根据表单已经滚动的高度,判断激活哪个锚点

遍历锚点列表,如果符合以下条件,则修改激活的锚点项

  • 如果 表单滚动的高度 等于 表单标题的 offsetTop
  • 如果 表单滚动的高度 介于 当前标题节点的 offsetTop 和 下一个标题节点的 offsetTop 之间(也就是当前标题看不到了,但下一个标题还没滚动到头部)

注意:由第二条可知,我们要对比下一个节点和当前节点的 offsetTop,所以最后一个节点不可以用上述方法判断是否激活

如何判断最后一个节点呢?

如果当前表单滚动的高度 大于 最后一个标题节点的 offsetTop,则直接激活

注意:这个判断方法存在 bug,如果最后的表单内容没有那么厂,就永远不会激活最后一个节点,但是目前没找到好的解决方案

/**
 * 根据表单已经滚动的高度,判断激活哪个锚点
 */
const activeFixedAnchor = () => {
  // 这里需要注意一个问题,表单实例的 scrollTop 是相对于编辑页面头部的下方开始的,而标题的 offsetTop 是相对于 微应用容器 计算的,因此要加上 65
  const formScrollTop = props.formDom.scrollTop + 65; // 表单的 scrollTop,默认为 0

  for (let k = 0; k < state.anchorList.length; k++) {
    if (
      // 如果 scrollTop 正好和标题节点的 offsetTop 相等
      formScrollTop === state.anchorList[k].top
      // 由于需要和下一个标题节点作比较,所以当前标题节点不能是最后一个
      || (k < state.anchorList.length - 1
        // scrollTop 介于当前判断的标题节点和下一个标题节点之间
        && formScrollTop > state.anchorList[k].top
        && formScrollTop < state.anchorList[k + 1].top)
    ) {
      // console.log('表单的 scrollTop,激活标题的 offsetTop,激活id ===', formScrollTop, state.anchorList[k].top, k);
      state.currentAnchor = k;
      break;
      // 如果是最后一个标题节点,只要 scrollTop 大于节点的 offsetTop 即可
    } else if (k === state.anchorList.length - 1) {
      if (formScrollTop > state.anchorList[k - 1].top) {
        state.currentAnchor = k;
        break;
      }
    }
  }
};

2.6 添加锚点项点击事件

参考 2.4.2 逻辑,基本一致

/**
 * 点击锚点列表项
 */
const handleAnchorClick = (anchorInfo: any) => {
  // console.log('当前点击的锚点列表项 ===', anchorInfo);

  // 修改当前选中的锚点
  state.currentAnchor = anchorInfo.index;
  // 即将滚动到的目标章节 DOM
  let showTitleDom: any;
  state.titleListInForm.forEach((item: any, index) => {
    const labelTemp = item.innerHTML;
    if (anchorInfo.label === labelTemp) {
      showTitleDom = item;
    }
  });
  // 如果找到了符合条件的章节 DOM
  if (showTitleDom) {
    // 平滑滚动
    showTitleDom.scrollIntoView({
      behavior: 'smooth',
      block: 'start',
    });
  }
};

2.7 实现返回顶部按钮功能

修改表单的 scrollTop 即可

/**
 * 返回顶部
 */
const handleReturnTop = () => {
  // eslint-disable-next-line no-param-reassign, vue/no-mutating-props
  props.formDom.scrollTop = 0;
};

2.8 最终代码

<template>
  <div class="anchor__container">
    <template v-if="anchorList.length">
      <div
        v-for="node in anchorList"
        :key="node.index"
        class="anchor__item"
        :label="node.label"
        :class="{ active: currentAnchor === node.index }"
        @click="handleAnchorClick(node)"
      >
        {{ node.label }}
      </div>
    </template>

    <div class="anchor__return-top" @click="handleReturnTop">
      返回顶部
    </div>
  </div>
</template>

<script lang="ts">
import { reactive, toRefs, defineComponent, onMounted, onUnmounted } from 'vue';
// hooks 防抖
import { useDebounce } from '../hooks/common/use-debounce';

export default defineComponent({
  name: 'AnchorPoint',
  props: {
    // 使用锚点的表单实例
    formDom: {
      type: Object,
      default: () => ({}),
      required: true,
    },
    // 默认激活哪个锚点(项目中有要求一进入某个表单时,就定位到指定锚点的需求,默认激活第一个节点)
    activeAnchor: {
      type: Number,
      default: 0,
    },
    // 章节特有的类名(用于判断元素的 offsetTop,5.0 里使用 .details-container__submenu 作为章节类名,也可以自己定义)
    titleClass: {
      type: String,
      default: '.details-container__submenu',
    },
  },
  setup(props, { emit }) {
    // 响应式变量
    const state = reactive({
      // 锚点列表
      anchorList: [] as any[],
      // 当前激活的锚点索引
      currentAnchor: 0,
      // 表单实例中,章节 DOM 列表(锚点列表的内容就是通过这个变量填充的)
      titleListInForm: [] as any[],
    });

    /**
     * 返回顶部
     */
    const handleReturnTop = () => {
      // eslint-disable-next-line no-param-reassign, vue/no-mutating-props
      props.formDom.scrollTop = 0;
    };

    /**
     * 从表单实例中,获取章节列表,并填充锚点列表
     */
    const getAnchorList = () => {
      // 清空锚点列表
      state.anchorList = [];

      // 获取表单实例中的章节 DOM 列表
      state.titleListInForm = Array.from(props.formDom.querySelectorAll(props.titleClass));
      // console.log('获取表单实例中的章节 DOM 列表 titleListInForm ===', titleListInForm);

      // 遍历章节 DOM 列表,填充锚点列表
      state.titleListInForm.forEach((item: any, index) => {
        // console.log('当前遍历的 章节 DOM item ===', item);
        state.anchorList.push({
          index, // 章节索引
          label: item.innerHTML || '--', // 章节内容
          top: item.offsetTop,
          titleDOM: item, // 章节完整 DOM 信息
        });
      });
      // console.log('填充锚点列表 state.anchorList ===', state.anchorList);
    };

    /**
     * 点击锚点列表项
     */
    const handleAnchorClick = (anchorInfo: any) => {
      // console.log('当前点击的锚点列表项 ===', anchorInfo);

      // 修改当前选中的锚点
      state.currentAnchor = anchorInfo.index;
      // 即将滚动到的目标章节 DOM
      let showTitleDom: any;
      state.titleListInForm.forEach((item: any, index) => {
        const labelTemp = item.innerHTML;
        if (anchorInfo.label === labelTemp) {
          showTitleDom = item;
        }
      });
      // 如果找到了符合条件的章节 DOM
      if (showTitleDom) {
        // 平滑滚动
        showTitleDom.scrollIntoView({
          behavior: 'smooth',
          block: 'start',
        });
      }
    };

    /**
     * 根据表单已经滚动的高度,判断激活哪个锚点
     */
    const activeFixedAnchor = () => {
      // 这里需要注意一个问题,表单实例的 scrollTop 是相对于编辑页面头部的下方开始的,而标题的 offsetTop 是相对于 微应用容器 计算的,因此要加上 65
      const formScrollTop = props.formDom.scrollTop + 65; // 表单的 scrollTop,默认为 0

      for (let k = 0; k < state.anchorList.length; k++) {
        if (
          // 如果 scrollTop 正好和标题节点的 offsetTop 相等
          formScrollTop === state.anchorList[k].top
          // 由于需要和下一个标题节点作比较,所以当前标题节点不能是最后一个
          || (k < state.anchorList.length - 1
            // scrollTop 介于当前判断的标题节点和下一个标题节点之间
            && formScrollTop > state.anchorList[k].top
            && formScrollTop < state.anchorList[k + 1].top)
        ) {
          // console.log('表单的 scrollTop,激活标题的 offsetTop,激活id ===', formScrollTop, state.anchorList[k].top, k);
          state.currentAnchor = k;
          break;
          // 如果是最后一个标题节点,只要 scrollTop 大于节点的 offsetTop 即可
        } else if (k === state.anchorList.length - 1) {
          if (formScrollTop > state.anchorList[k - 1].top) {
            state.currentAnchor = k;
            break;
          }
        }
      }
    };

    /**
     * 处理滚动事件
     */
    const handleScroll = (e: any) => {
      // console.log('处理滚动事件', e);
      e.stopPropagation();
      // 根据表单已经滚动的高度,判断激活哪个锚点
      activeFixedAnchor();
    };

    /**
     * 对滚动事件进行防抖处理,节约性能
     */
    const handleDebounceScroll = useDebounce(handleScroll, 200);

    onMounted(() => {
      // console.log('锚点组件内,获取滚动表单实例 ===', props.formDom);

      // 从表单实例中,获取章节列表,并填充锚点列表
      getAnchorList();

      // 如果默认激活的锚点,不是第一个,则要先进行一次滚动
      if (props.activeAnchor !== 0) {
        state.currentAnchor = props.activeAnchor;
        // 即将滚动到的目标章节 DOM
        let showTitleDomStart: any;
        state.anchorList.forEach((item: any) => {
          const indexTemp = item.index;
          if (props.activeAnchor === indexTemp) {
            showTitleDomStart = item.titleDOM;
            console.log('默认滚动到的 章节DOM', item.titleDOM);
          }
        });
        // 如果找到了符合条件的章节 DOM
        if (showTitleDomStart) {
          setTimeout(() => {
            // 平滑滚动
            showTitleDomStart.scrollIntoView({
              behavior: 'smooth',
              block: 'start',
            });
          }, 500);
        }
      }

      // 给表单添加滚动监听
      props.formDom.addEventListener('scroll', handleDebounceScroll);
    });

    onUnmounted(() => {
      // 移除表单滚动监听
      props.formDom.removeEventListener('scroll', handleDebounceScroll);
    });

    return {
      ...toRefs(state),
      handleReturnTop,
      handleAnchorClick,
    };
  },
});
</script>

<style lang="scss" scoped>
.anchor__container {
  position: fixed;
  top: 50%;
  right: 46px;
  overflow: auto;
  box-sizing: border-box;
  width: 300px;
  height: 180px;
  padding: 12px;
  background: rgba(255, 0, 0, 0.4);
  transform: translate(0, -50%);
}

.anchor__item {
  overflow: hidden;
  box-sizing: border-box;
  width: 100%;
  margin: 4px 0;
  padding: 4px;
  background: rgba(255, 255, 0, 0.2);
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.anchor__return-top {
  position: absolute;
  bottom: 0;
  padding: 4px 0;
  background: rgba(0, 0, 255, 0.2);
  color: blue;
  cursor: pointer;
}

.active {
  color: yellow;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/10961.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5-FITC,5-FITC(isomer I),5-异硫氰酸荧光素,5-Flourescein iso-thiocyanate

产品名称&#xff1a;5-FITC&#xff0c;5-异硫氰酸荧光素 英文名称&#xff1a;5-Flourescein iso-thiocyanate 英文别名&#xff1a;5-FITC&#xff1b;5-Flourescein iso-thiocyanate&#xff1b;FITC Isomer I [5-FITC; fluorescein-5-isothiocyanate] CAS#&#xff1a;…

labview 串口通信 modbusRtu

在自动化或测试项目中&#xff0c;上位机软件需要和PLC及仪表通信&#xff0c;本文简单描述这个问题。 1.在程序框图中放置4个图标 &#xff08;1&#xff09;创建modbus 主站实例&#xff08;按如下图标识①,在框图中放Create Master Instance.vi) 图1 放置四个图标 &…

C++ Reference: Standard C++ Library reference: Containers: deque: deque: resize

C官网参考链接&#xff1a;https://cplusplus.com/reference/deque/deque/resize/ 公有成员函数 <deque> std::deque::resize C98 void resize (size_type n, value_type val value_type()); C11 void resize (size_type n); void resize (size_type n, const value_t…

React组件复用

mixins&#xff08;已废弃&#xff09; https://react.docschina.org/blog/2016/07/13/mixins-considered-harmful.html mixin引入了隐式依赖关系 对于组件中的方法和数据的来源不明确&#xff0c;不容易维护 Mixins 导致名称冲突Mixins 导致滚雪球般的复杂性 render-props技术…

C语言学习之路(基础篇)—— 指针(上)

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 概述 1) 内存 内存含义&#xff1a; 存储器&#xff1a; 计算机的组成中&#xff0c;用来存储程序和数据&#xff0c;辅助CPU进行运算处理的重要…

python切分TXT的句子到Excel(复制可用)——以及python切分句子遇到的问题汇总

文章目录完整代码时间转化和提取各种对象类型转换时间序列类属性数据转换完整代码 import jieba.analyseimport jieba.posseg as pseg from wordcloud import WordCloud import xlsxwriter # encodinggbk import xlsxwriterf open(E:/data/xieyangteng/review.txt, r, encodi…

波的相关参数概念整理

频率&#xff08;frequency&#xff09;&#xff0c;符号f&#xff0c;表示单位时间内完成周期性变化的次数。f1/T&#xff0c;单位s-1 角频率&#xff0c;符号ω&#xff0c;表示单位时间内变化的角弧度值。ω 2πf 2π/T,单位rad/s 波长&#xff08;wavelength&#xff0…

<SQL编程工具MySQL、SQLyog安装及环境配置教程>——《SQL》

目录 1.MySQL安装&#xff1a; 1.1 MySQL下载安装&#xff1a; 1.2 MySQL环境变量配置&#xff1a; 2.SQLyog安装&#xff1a; 2.1 SQLyog下载安装&#xff1a; 3.写在最后的话&#xff1a; 后记&#xff1a;●由于作者水平有限&#xff0c;文章难免存在谬误之处&…

力扣刷题day49|647回文子串、516最长回文子序列

文章目录647. 回文子串思路暴力解法动态规划五部曲516. 最长回文子序列思路动态规划五部曲647. 回文子串 力扣题目链接 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由…

代码随想录算法训练营第一天|LeetCode704二分查找、LeetCode27移除元素

LeetCode704二分查找 题目链接&#xff1a;704二分查找 思路&#xff1a; 以前刷过不少题&#xff0c;也看过不少题解&#xff0c;就记得区间有不少原则&#xff0c;乍一想有哪些想不起来了&#xff0c;反正我是选择了最简单易懂的左闭右闭原则。 1、区间左闭右闭原则。 2、w…

SpringBoot SpringBoot 开发实用篇 2 配置高级 2.3 常用计量单位应用

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇2 配置高级2.3 常用计量单位应用2.3.1 问题引入2.3.2 常用计量单位应…

实验2 存储器设计与实现【计算机组成原理】

实验2 存储器设计与实现【计算机组成原理】实验2 存储器设计与实现一、实验目的二、实验环境三、实验原理四、实验任务五、实验结果&#xff1a;六、心得体会&#xff1a;实验2 存储器设计与实现 一、实验目的 掌握单端口RAM和ROM原理和设计方法。掌握32位数据的读出和写入方…

【LeetCode-中等】343. 整数拆分(详解)

题目 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 力扣&#xff1a;题目链接 方法1&#xff1a;动态规划 完全不了解动态规划&#xff1f; 动态规划…

图像运算和图像增强九

图像运算和图像增强九 图像锐化之 Roberts、Prewitt 算子实现边缘检测 &#xff08;1&#xff09;图像锐化 图像锐化的目的是为了使图像的边缘、轮廓线以及图像的细节变得清晰&#xff0c;经过平滑的图像变得模糊的根本原因是图像受到了平均或积分运算&#xff0c;因此可以对…

LeetCode刷题(python版)——Topic70. 爬楼梯

一、题设 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例…

Flutter混编之路IOS插件0基础开发(mac环境)

本文默认你安装了Android Studio、Xcode具备flutter开发环境&#xff0c;并且会dart语言的基础使用&#xff0c;Android、ios原生开发不会也没啥关系&#xff0c;就是会很费劲啦。 1.创建插件 在Android studio 点击File-->new Flutter Project-->Flutter 取好名字&…

Web大学生网页作业成品:基于html制作中国科技发展网站设计题材【航天之路7页】HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Celery快速使用(定时任务、Django中使用Celery、秒杀逻辑、双写一致性)

文章标题一、Celery快速使用二、Celery包结构三、Celery异步任务 延时任务 定时任务四、Django中使用Celery五、秒杀逻辑六、双写一致性1&#xff09;路飞项目接口加缓存2&#xff09;Celery定时任务实现双写一致性一、Celery快速使用 简单介绍Celery Celery官网:http://www.…

busybox的实现原理分析(C语言实现简易版的busybox)

1、linux中实现命令的两种方式 1.1、命令都是单独的可执行程序 aston:~$ ls -l /bin/ls -rwxr-xr-x 1 root root 138208 2鏈 8 2022 /bin/ls aston:~$ aston:~$ ls -l /bin/mkdir -rwxr-xr-x 1 root root 68096 2鏈 8 2022 /bin/mkdir aston:~$ aston:~$ file /bin/ls…

元数据性能大比拼:HDFS vs S3 vs JuiceFS

元数据是存储系统的核心大脑&#xff0c;元数据性能对整个大数据平台的性能和扩展能力至关重要。尤其在处理海量文件的时候。在平台任务创建、运行和结束提交阶段&#xff0c;会存在大量的元数据 create&#xff0c;open&#xff0c;rename 和 delete 操作。因此&#xff0c;在…