UI原型
HTML代码
CSS代码
*{
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
input {
outline: none;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
text-indent: 8px;
}
[v-cloak]{display: none;}
.hide{display: none !important;}
.scroll-table-box {
position: relative;
}
.scroll-table-head {
width: 100%;
overflow: hidden;
}
.scroll-table-body {
max-height: calc(100vh - 271px);
overflow: hidden;
}
.no_border_top{
border-top: none !important;
}
.w_60 {
width: 60px !important;
}
.scroll-table-box .v-shadow {
position: absolute;
left: 129px;
top: 0;
width: 0px;
height: 100%;
z-index: -1;
-webkit-box-shadow: -1px 3px 10px 1px #000000;
-moz-box-shadow: -1px 3px 10px 1px #000000;
box-shadow: -1px 3px 10px 1px #000000;
}
.scroll-table-fixed-left {
width: 43px;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow: hidden;
}
.scroll-table-fixed-left .scroll-table-fixed-left-head {
position: relative;
}
.scroll-table-fixed-left .scroll-table-fixed-left-body {
position: relative;
max-height: calc(100% - 39px);
overflow: hidden;
}
.scroll-table-box .tb1{
width: 100%;
table-layout: fixed;
border-top: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.scroll-table-box .tb1 th,.scroll-table-box .tb1 td{
padding: 8px 4px;
border-bottom: 1px solid #ccc;
width: 100px;
text-align: center;
font-size: 14px;
}
.scroll-table-box .tb1 th {
background-color: #edf6fd;
color: #333;
border-left: 1px solid #ccc;
}
.scroll-table-box .tb1 td{
background-color: #fff;
color: black;
border-left: 1px solid #ccc;
}
.scroll-table-box .tb1 td .td_div{
line-height: 20px;
text-align: center;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-word;
}
.scroll-table-box .tb1 td .td-input {
width: 60px;
color: red;
text-align:center;
text-indent: 0px;
border: 1px solid #A5A5A5;
}
.td-grey {
background-color: #f8f8f8 !important;
}
js代码
move.js
(function(){
window.requestAnimationFrame = window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame|| window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame||window.cancelRequestAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame;
if(!requestAnimationFrame){
var lastTime = Date.now();
window.requestAnimationFrame = function(callback){
var id;
var nowTime = Date.now();
var delay = Math.max(16.7-(nowTime-lastTime),0);
id = setTimeout(callback,delay);
lastTime = nowTime + delay;
return id;
};
}
if(!cancelAnimationFrame){
window.cancelAnimationFrame = function(index){
clearTimeout(index);
};
}
})();
/*
startMove(init)
init:{
el: 要动画的元素,
type: 动画的类型,
target: {//目标值
left: 100,
translateX: 100
},
time: //动画时长ms,
callBack: fn //动画执行完成之后 要执行的函数
callIn: fn//动画执行中
}
*/
function transform(el,attr,val){
if(!el.transform){
el.transform = {
};
}
if(val === undefined){
return el.transform[attr];
}
el.transform[attr] = val;
var str = "";
for(var s in el.transform){
switch(s){
case "rotate":
case "rotateX":
case "rotateY":
case "rotateZ":
case "skewX":
case "skewY":
str += s +"("+el.transform[s]+"deg) ";
break;
case "scale":
case "scaleX":
case "scaleY":
str += s +"("+el.transform[s]+") ";
break;
case "translateX":
case "translateY":
case "translateZ":
str += s +"("+el.transform[s]+"px) ";
break;
}
}
el.style.WebkitTransform = el.style.transform = str;
}
function css(el,attr,val){
var transformAttr = ["rotate","rotateX","rotateY","rotateZ","skewX","skewY","scale","scaleX","scaleY","translateX","translateY","translateZ"];
for(var i = 0; i < transformAttr.length; i++){
if(attr == transformAttr[i]){ //如果 attr 等transform其中一个值就代表用用户想要操作的是 transform
return transform(el,attr,val);
}
}
if(val === undefined){
val = getComputedStyle(el)[attr];
// console.log(val);
val = parseFloat(val);
return val;
}
if(attr == "opacity"){
el.style[attr] = val;
} else {
el.style[attr] = val + "px";
}
}
function startMove(init){
var t = 0;
var b = {};//样式的初始值
var c = {};//样式的差值
var d = Math.ceil(init.time/16.7);
cancelAnimationFrame(init.el.timer);
for(var s in init.target) {
b[s] = css(init.el,s);
c[s] = init.target[s] - b[s];
}
init.el.timer = requestAnimationFrame(move);
function move(){
if(t > d || d == 0){
cancelAnimationFrame(init.el.timer);
init.callBack&&init.callBack.call(init.el);
} else {
t++;
for(var s in init.target){
var val = Tween[init.type](t,b[s],c[s],d);
css(init.el,s,val);
}
init.callIn&&init.callIn.call(init.el);
init.el.timer = requestAnimationFrame(move);
}
}
}
tween.js
var Tween = {
linear: function (t, b, c, d){
return c*t/d + b;
},
easeIn: function(t, b, c, d){
return c*(t/=d)*t + b;
},
easeOut: function(t, b, c, d){
return -c *(t/=d)*(t-2) + b;
},
easeBoth: function(t, b, c, d){
if ((t/=d/2) < 1) {
return c/2*t*t + b;
}
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInStrong: function(t, b, c, d){
return c*(t/=d)*t*t*t + b;
},
easeOutStrong: function(t, b, c, d){
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeBothStrong: function(t, b, c, d){
if ((t/=d/2) < 1) {
return c/2*t*t*t*t + b;
}
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
elasticIn: function(t, b, c, d, a, p){
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p/4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
elasticOut: function(t, b, c, d, a, p){
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p / 4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
elasticBoth: function(t, b, c, d, a, p){
if (t === 0) {
return b;
}
if ( (t /= d/2) == 2 ) {
return b+c;
}
if (!p) {
p = d*(0.3*1.5);
}
if ( !a || a < Math.abs(c) ) {
a = c;
var s = p/4;
}
else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
if (t < 1) {
return - 0.5*(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
}
return a*Math.pow(2,-10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
},
backIn: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158;
}
return c*(t/=d)*t*((s+1)*t - s) + b;
},
backOut: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158; //回缩的距离
}
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
backBoth: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158;
}
if ((t /= d/2 ) < 1) {
return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
}
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
bounceIn: function(t, b, c, d){
return c - Tween['bounceOut'](d-t, 0, c, d) + b;
},
bounceOut: function(t, b, c, d){
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
}
return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
},
bounceBoth: function(t, b, c, d){
if (t < d/2) {
return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;
}
return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
}
};
主要脚本代码(以VUE框架为例)
mounted: function() {
var self = this;
this.$nextTick(function () {
//表格主区域滑动
self.tableSwiperFunc({
$scrollTableFixedLeft: $(".scroll-table-fixed-left"),
wrap: $(".scroll-table-body")[0],
scroll: $(".scroll-table-body table")[0],
wrap_one: $(".scroll-table-fixed-left-body")[0],
scroll_one: $(".scroll-table-fixed-left-body table")[0],
wrap_two: $(".scroll-table-head")[0],
scroll_two: $(".scroll-table-head table")[0],
judgeScroll: $(".scroll-table-body table")[0],
moveFunc: function (isDir, target, scroll, scroll_one, scroll_two) {
if (isDir.x) {
transform(scroll, 'translateX', target.x);
transform(scroll_two, 'translateX', target.x);
} else if (isDir.y) {
transform(scroll, 'translateY', target.y);
transform(scroll_one, 'translateY', target.y);
}
},
endFunc: function (isDir, targetX, targetY, time, scroll, scroll_one, scroll_two) {
if (isDir.x) {
startMove({
el: scroll,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
startMove({
el: scroll_two,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
} else if (isDir.y) {
startMove({
el: scroll,
type: "easeOutStrong",
time: time.timeY,
target: {
translateY: targetY
}
});
startMove({
el: scroll_one,
type: "easeOutStrong",
time: time.timeY,
target: {
translateY: targetY
}
});
}
}
});
//表格左边固定表头区域滑动
self.tableSwiperFunc({
$scrollTableFixedLeft: $(".scroll-table-fixed-left"),
wrap: $(".scroll-table-fixed-left-body")[0],
scroll: $(".scroll-table-fixed-left-body table")[0],
wrap_one: $(".scroll-table-body")[0],
scroll_one: $(".scroll-table-body table")[0],
wrap_two: $(".scroll-table-head")[0],
scroll_two: $(".scroll-table-head table")[0],
judgeScroll: $(".scroll-table-body table")[0],
wrapIsFixedLeft: true,
moveFunc: function (isDir, target, scroll, scroll_one, scroll_two) {
if (isDir.x) {
transform(scroll_one, 'translateX', target.x);
transform(scroll_two, 'translateX', target.x);
}
if (isDir.y) {
transform(scroll, 'translateY', target.y);
transform(scroll_one, 'translateY', target.y);
}
},
endFunc: function (isDir, targetX, targetY, time, scroll, scroll_one, scroll_two) {
if (isDir.x) {
startMove({
el: scroll_one,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
startMove({
el: scroll_two,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
}
if (isDir.y) {
startMove({
el: scroll,
type: "easeOutStrong",
time: time.timeY,
target: {
translateY: targetY
}
});
startMove({
el: scroll_one,
type: "easeOutStrong",
time: time.timeY,
target: {
translateY: targetY
}
});
}
}
});
//表格固定头部表头区域滑动
self.tableSwiperFunc({
$scrollTableFixedLeft: $(".scroll-table-fixed-left"),
wrap: $(".scroll-table-head")[0],
scroll: $(".scroll-table-head table")[0],
wrap_one: $(".scroll-table-fixed-left-body")[0],
scroll_one: $(".scroll-table-fixed-left-body table")[0],
wrap_two: $(".scroll-table-body")[0],
scroll_two: $(".scroll-table-body table")[0],
judgeScroll: $(".scroll-table-body table")[0],
moveFunc: function (isDir, target, scroll, scroll_one, scroll_two) {
if (isDir.x) {
transform(scroll, 'translateX', target.x);
transform(scroll_two, 'translateX', target.x);
}
},
endFunc: function (isDir, targetX, targetY, time, scroll, scroll_one, scroll_two) {
if (isDir.x) {
startMove({
el: scroll,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
startMove({
el: scroll_two,
type: "easeOutStrong",
time: time.timeX,
target: {
translateX: targetX
}
});
}
}
});
$(".bug-tr").css({
'line-height': "42px"
});
setTimeout(function () {
$(".bug-tr").css({
'line-height': "43px"
});
}, 1000);
})
},
updated: function () {
this.$nextTick(function () {
$('.scroll-table-fixed-left-body tr').each(function (index, item) {
var h = $('.scroll-table-body tr').eq(index).height();
$(item).find('td').outerHeight(h);
});
})
},
methods: {
/**
* 表格区域滑动
* init:{
* $scrollTableFixedLeft: 左边固定表头(jq选择器),
* wrap: 主滑动区域(父级),
* scroll: 主滑动区域,
* wrap_one: 副滑动区域1(父级),
* scroll_one: 副滑动区域1,
* wrap_two: 副滑动区域2(父级),
* scroll_two: 副滑动区域2,
* judgeScroll: 某元素控制左侧固定菜单是否显示,
* wrapIsFixedLeft: true||false,//手指是否在左侧固定表头滑动
* moveFunc: function(){},//touchmove的回调
* endFunc: function(){},//touchend的回调
* }
*/
tableSwiperFunc: function(init) {
var self = this;
var $vShadow = $(".v-shadow");
var $scrollTableFixedLeft = init.$scrollTableFixedLeft;
var judgeScroll = init.judgeScroll;
/*表格主滑动区域*/
var wrap = init.wrap;
var scroll = init.scroll;
var wrap_width = 0;
var scroll_width = 0;
var wrap_height = 0;
var scroll_height = 0;
/*表格副滑动区域1*/
var wrap_one = init.wrap_one;
var scroll_one = init.scroll_one;
var wrap_width_one = 0;
var scroll_width_one = 0;
var wrap_height_one = 0;
var scroll_height_one = 0;
/*表格副滑动区域2*/
var wrap_two = init.wrap_two;
var scroll_two = init.scroll_two;
var wrap_width_two = 0;
var scroll_width_two = 0;
var wrap_height_two = 0;
var scroll_height_two = 0;
var isDir = { // 判断滑动的方向
x: false,
y: false
};
var isFirst = true;
var startPonit = {};
var startEl = {};
var startTime = 0;
var dis = {}; // 手指 当前值 和 初始值的一个差值
var timeDis = 0; // 时间差值
/*设置初始transfrom*/
transform(scroll, "translateX", 0);
transform(scroll, "translateY", 0);
transform(scroll_one, "translateX", 0);
transform(scroll_one, "translateY", 0);
transform(scroll_two, "translateX", 0);
transform(scroll_two, "translateY", 0);
wrap.addEventListener('touchstart', function (e) {
var touch = e.changedTouches[0];
wrap_width = $(wrap).width();
scroll_width = $(scroll).width();
wrap_height = $(wrap).height();
scroll_height = $(scroll).height();
wrap_width_one = $(wrap_one).width();
scroll_width_one = $(scroll_one).width();
wrap_height_one = $(wrap_one).height();
scroll_height_one = $(scroll_one).height();
wrap_width_two = $(wrap_two).width();
scroll_width_two = $(scroll_two).width();
wrap_height_two = $(wrap_two).height();
scroll_height_two = $(scroll_two).height();
scroll.style.transition = "none";
scroll.style.WebkitTransition = "none";
scroll_one.style.transition = "none";
scroll_one.style.WebkitTransition = "none";
scroll_two.style.transition = "none";
scroll_two.style.WebkitTransition = "none";
startPonit = {
x: touch.pageX,
y: touch.pageY
};
startEl = {
x: transform(scroll, "translateX"),
y: transform(scroll, "translateY")
};
if (init.wrapIsFixedLeft) {
startEl = {
x: transform(scroll_one, "translateX"),
y: transform(scroll, "translateY")
};
}
startTime = Date.now();
});
wrap.addEventListener('touchmove', function (e) {
e.preventDefault();
var touch = e.changedTouches[0];
var nowPonit = {
x: touch.pageX,
y: touch.pageY
};
var nowTime = Date.now();
/* 手指 当前值 和 初始值的一个差值 */
dis = {
x: nowPonit.x - startPonit.x,
y: nowPonit.y - startPonit.y
};
/*确定滑动方向*/
if (isFirst) {
if (Math.abs(dis.x) >= Math.abs(dis.y)) {
isDir.x = true;
isFirst = false;
} else if (Math.abs(dis.x) < Math.abs(dis.y)) {
isDir.y = true;
isFirst = false;
}
}
/* 时间差值 */
timeDis = nowTime - startTime;
/* 元素应在要在的一个位置 */
var target = {
x: Math.round(startEl.x + dis.x),
y: Math.round(startEl.y + dis.y)
};
if (transform(judgeScroll, "translateX") < 0) {
$scrollTableFixedLeft.removeClass('hide');
} else if (transform(judgeScroll, "translateX") >= 0) {
$scrollTableFixedLeft.addClass('hide');
}
/* 设置样式 */
init.moveFunc && init.moveFunc(isDir, target, scroll, scroll_one, scroll_two);
});
wrap.addEventListener('touchend', function (e) {
var x_bottom_back_num = -(scroll_width - wrap_width); // X轴触底值
var y_bottom_back_num = -(scroll_height - wrap_height); // Y轴触底值
if (timeDis >= 100) {// 判断 当用户手指抬起时 和 最后一次移动的时候,有比较大的一个时间间隔,就可以认定 用户在抬起手指前有那么一段时间是按着不动的,那么我们也就不执行缓冲
dis.x = 0;
dis.y = 0;
}
var speedX = dis.x / timeDis; // X轴速度
var speedY = dis.y / timeDis; // Y轴速度
speedX = speedX ? speedX : 0;
speedY = speedY ? speedY : 0;
// var s = speed*speed / (2*0.005) * (speed/Math.abs(speed)); // 距离
var sX = speedX * 170;
var sY = speedY * 170;
var nowX = transform(scroll, "translateX");
var nowY = transform(scroll, "translateY");
var targetX = Math.round(nowX + sX);
var targetY = Math.round(nowY + sY);
//过界处理
if (targetX > 0) {
targetX = 0;
} else if (scroll_width >= wrap_width && targetX < x_bottom_back_num) {
targetX = x_bottom_back_num;
}
if (targetY > 0) {
targetY = 0;
} else if (scroll_height >= wrap_height && targetY < y_bottom_back_num) {
targetY = y_bottom_back_num;
}
// time 整个动画的动画时间
// s - now 移动距离
// time 移动距离越远 时间就越长
var timeX = Math.abs(sX) * 1.15;
var timeY = Math.abs(sY) * 1.15;
timeX = timeX == 0 ? 200 : timeX;
timeY = timeY == 0 ? 200 : timeY;
var time = {
timeX: timeX,
timeY: timeY
};
if (init.wrapIsFixedLeft) {
x_bottom_back_num = -(scroll_width_one - wrap_width_one);
nowX = transform(scroll_one, "translateX");
targetX = Math.round(nowX + sX);
if (targetX > 0) {
targetX = 0;
} else if (scroll_width_one >= wrap_width_one && targetX < x_bottom_back_num) {
targetX = x_bottom_back_num;
}
}
init.endFunc && init.endFunc(isDir, targetX, targetY, time, scroll, scroll_one, scroll_two);
isFirst = true;
isDir = { // 初始化滑动的方向
x: false,
y: false
};
});
}
}