前言
之所以要做这个工具,是因为最近工作中需要处理大量带有时间戳的数据,而时间戳又不够明了,在开发、测试时就不那么方便。网上也有时间戳的转换工具,但都是单个的,不能批量转换,索性就自己动手做一个。同时增加了一些动画效果来让这个工具变得精致一些。
在线体验
cmyayjd.github.io/timestamp-c…
一睹为快
可以看到这个工具主要有以下几个功能:
- 顺滑的加载动画
- 精致的自定义cursor
- 自定义textarea的placeholder,具有渐隐渐现的效果
- 批量转换timestamp为date
- 导入.txt文件转换timestamp
- 拖动.txt文件转换timestamp
- 清空
下面就一个个来实现
加载动画
<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。