提高工作效率 — 做个精致的、可拖拽的、时间戳批量转换工具

1,345 阅读4分钟

前言

之所以要做这个工具,是因为最近工作中需要处理大量带有时间戳的数据,而时间戳又不够明了,在开发、测试时就不那么方便。网上也有时间戳的转换工具,但都是单个的,不能批量转换,索性就自己动手做一个。同时增加了一些动画效果来让这个工具变得精致一些。

在线体验

cmyayjd.github.io/timestamp-c…

一睹为快

可以看到这个工具主要有以下几个功能:

  1. 顺滑的加载动画
  2. 精致的自定义cursor
  3. 自定义textarea的placeholder,具有渐隐渐现的效果
  4. 批量转换timestamp为date
  5. 导入.txt文件转换timestamp
  6. 拖动.txt文件转换timestamp
  7. 清空

下面就一个个来实现

加载动画

<div id="app" ref="app">
    <header class="header">
        <div class="logo"></div>
        <div class="loader"></div>
    </header>
    <div class="main">
        <div class="logo"></div>
        <h1>时间戳批量转换小工具</h1>
        <div class="text-container"></div>
        <div class="action-btns"></div>
    </div>
</div>

先来看下页面的结构,分为header和main两部分,一开始,header是盖在main上的。加载动画就是移除header并显示main的过程。整个加载分为两个阶段:loading和loaded,分别表示加载中和加载完成。loader转圈开始到转圈结束是loading阶段,接着就是loaded阶段。具体实现上是通过给app添加loading和loaded两个样式类实现的。因为样式文件有点长,这里就不贴了。

initLoad(){
  const app = this.$refs.app
  app.classList.add('loading')
  const loader = new Path(this.$refs.loader)
  let progress = 0
  let interval = setInterval(() => {
    progress = Math.min(progress + Math.random() * 0.1, 1)
    loader.progress(progress)

    if(progress == 1){
      app.classList.remove('loading')
      app.classList.add('loaded')
      clearInterval(interval)
    }
  }, 100)
},

这里的loader转圈动画是通过svg的path实现的

<div class="loader">
    <svg width="60px" height="60px" viewBox="0 0 80 80">
      <path class="loader-bg" d="M40,10C57.351,10,71,23.649,71,40.5S57.351,71,40.5,71 S10,57.351,10,40.5S23.649,10,40.5,10z"/>
      <path id="loader-circle" ref="loader" d="M40,10C57.351,10,71,23.649,71,40.5S57.351,71,40.5,71 S10,57.351,10,40.5S23.649,10,40.5,10z"/>
    </svg>
</div>

export default class Path {
  constructor(el){
    this.el = el
    this.init()
  }

  init(){
    this.el.style.strokeDasharray = this.el.style.strokeDashoffset = this.el.getTotalLength()
  }

  render(val){
    this.el.style.strokeDashoffset = this.el.getTotalLength() * ( 1 - val )
  }

  progress = function( val, callback ) {
	this.render(val);
	if( callback && typeof callback === 'function' ) {
	    setTimeout( callback, 200 )
	}
  }
}

自定义cursor

自定义cursor其实只需要两步:一是设置cursor: none 二是动态修改自定义物体的位移。自定义的cursor分为两部分:图片和圆环

<div class="cursor cursor-point" ref="cursorPoint">
  <img alt="cursor img" src="./images/author.png" />
</div>
<div class="cursor cursor-round" ref="cursorRound">
  <div class="inner"></div>
</div>

监听鼠标移动事件,动态修改位移。

const lerp = (x, y, diff) => {
  return (1 - diff) * x + diff * y;
};

initCursor() {
  let clientX = -100;
  let clientY = -100;
  let lastX = 0;
  let lastY = 0;
  const cursorPoint = this.$refs.cursorPoint;
  const cursorRound = this.$refs.cursorRound;
  document.addEventListener("mousemove", (e) => {
    clientX = e.clientX;
    clientY = e.clientY;
  });
  const render = () => {
    cursorPoint.style.transform = `translate(${clientX}px, ${clientY}px)`;
    lastX = lerp(lastX, clientX - 16, 0.2);
    lastY = lerp(lastY, clientY - 16, 0.2);
    cursorRound.style.transform = `translate(${lastX}px, ${lastY}px)`;
    requestAnimationFrame(render);
  };
  requestAnimationFrame(render);
},

自定义placeholder

<textarea
    @focus="textareaFocus('left')"
    @blur="textareaBlur('left')"
    v-model="left.content"
  ></textarea>
  <p
    class="centHid"
    :class="{ show: !left.isFocus && left.content == '' }"
  >
    请把需要转换的文件拖进这里
  </p>
  
  textareaFocus(id) {
    this[id].isFocus = true;
  },
  textareaBlur(id) {
    this[id].isFocus = false;
  },
  
  .centHid {
    font-size: 14px;
    color: #c0c1c4;
    position: absolute;
    left: 13px;
    top: 10px;
    opacity: 0;
    transition: opacity 0.3s;
    &.show {
      opacity: 1;
    }
  }

这里使用了一个p标签来实现这个功能,给它设置一个透明度渐变的transition。同时监听获取、失去焦点事件来设置状态。因为只有当失去焦点并且内容为空时才显示。

批量转换timestamp

batchConvert() {
  let leftContent = this.left.content;
  if (leftContent != "") {
    let rightContent = leftContent.split("\n").map((timestap) => {
      return this.transTimestampToDate(timestap);
    });
    this.right.content = rightContent.join("\n");
  }
},

从左textarea中获取所有的timestamp,获取到的是一个字符串。接着以空格符转为数组并遍历这个数组,对每一个timestamp都调用transTimestampToDate函数进行转化。最后将转化后的结果再以空格join成字符串并赋值给右textarea。transTimestampToDate这个函数是真正将timestamp转为date的,它的实现也很简单。

transTimestampToDate(timestap) {
  let data = new Date(timestap * 1000);
  return (
    data.toLocaleDateString().replace(/\//g, "-") +
    " " +
    data.toTimeString().substr(0, 8)
  );
}

事实上转换的方法有多种,这里选择了比较简单的一种。需要注意的是,在不同的浏览器上toLocaleDateString的表现可能不一致。

.txt文件导入

<div class="loadFile">
  <label for="loadFileBtn">
    <input
      type="file"
      accept=".txt"
      id="loadFileBtn"
      ref="fileInput"
      @change="readFile"
    />
    <div class="button">选择文件</div>
  </label>
</div>

readFile() {
  let files = this.$refs.fileInput.files;
  if (files && files[0]) {
    this.readFromText(files[0]).then((result) => {
      this.left.content = result;
    });
  }
}

readFromText(file) {
  let reader = new FileReader();
  return new Promise((resolve, inject) => {
    reader.readAsText(file);
    reader.onload = function() {
      resolve(this.result);
    };
  });
}

文件导入按钮采用type="file"的input实现,监听change事件获得导入的文件。然后调用readFromText方法读取文件内容并复值给左textarea。readFromText方法是对文件读取的promise封装,在文件拖动那块也会调用。

.txt文件拖动

initDrag(){
  let drag = this.$refs.drag;
  drag.addEventListener("drop", (e) => {
    e.preventDefault();
    const file = e.dataTransfer.files[0];
    if (file) {
      this.readFromText(file).then((result) => {
        this.left.content = result;
      });
    }
  });
}

要实现文件的拖动,只需要监听drop事件,从e.dataTransder.files中就能得到拖入的文件。接着调用readFromText方法读取文件的内容赋值给左textarea。readFromText这个方法在之前已经介绍过。

清空

这个功能很简单,就是将两个textarea的内容置为空字符串即可。

clear() {
  this.left.content = "";
  this.right.content = "";
}

最后

这是我第一次在掘金发表文章,还请各位朋友们批评指正。如果还满意,还请不吝点个star。

github: github.com/cmyayjd/tim…