MVVM模式进度条与滚动条
1、MVVM模式介绍
- MVVM 模式,模型 (Model) -视图 (View) -视图模型 (ViewModel) : 为视图层 (View)量身定做一套视图模型 (ViewModel),并在视图模型 (ViewModel) 中创建属性和方法,为书图层 (View) 绑定数据 (Model) 并实现交互。
2、代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.first,.second,.third{
border: 1px solid #333;
border-radius: 5px;
height: 35px;
margin: 55px 50px 15px 50px;
width: 500px;
}
.second{
top:60px;
}
.third{
top:110px;
}
.ui-slider,.ui-progressbar{
position: relative;
box-sizing: border-box;
border: 2px solid #000;
}
.ui-slider>span,.ui-slider>em{
position:absolute;
}
.ui-slider>em{
top:-30px;
}
.ui-slider>span{
box-sizing: border-box;
top:-20%;
width: 16px;
height: 140%;
border: 2px solid #000;
border-radius: 6px;
background-color: rgba(139,69,19,0.6);
cursor: pointer;
}
.ui-slider>b{
position: absolute;
left:calc(100% + 20px);
top:calc(50% - 10px);;
}
.ui-slider>div{
height: 100%;
background-color: rgba(0,255,0,1);
position: absolute;
top:0;
left: 0;
}
.ui-progressbar>div{
height: 100%;
background-color: rebeccapurple;
}
.fourth{
top:200px;
}
.ui-slider-red>div{
background-color:rgba(255,0,0,1);
}
</style>
</head>
<body>
<div class="first" data-bind="type:'slider',data:demo1"></div>
<div class="second ui-slider-red" data-bind="type:'slider',data:demo2"></div>
<div class="third" data-bind="type:'progressbar',data:demo3"></div>
<script>
~(function(){
// 在闭包中获取全局变量,因为非浏览器环境下全局变量并非 Window
var window=this||(0,eval)('this');
// 获取页面字体大小,作为创建页面UI的尺寸参照物 ??? getComputedStyle
var FONTSIZE=function(){
// 获取元素字体大小并转化为整数
return parseInt(document.body.currentStyle?document.body.currentStyle['fontSize']:getComputedStyle(document.body,false)['fontSize']);
}();
// 视图模型对象 40.8
var VM=function(){
// 组件创建策略对象 ???策略对象
var Method={
/***
* 进度条组件创建方法
* dom 进度条容器
* data 进度条数据模型
**/
progressbar:function(dom,data){
// 进度条进度完成容器
var progress=document.createElement('div'),
// 数据模型数据,结构:{position:50}
param=data.data;
// 设置进度完成容器尺寸
progress.style.width=(param.position||100)+'%';
// 为进度条添加UI样式
dom.className+=' ui-progressbar';
// 进度完成容器元素插入进度条容器中
dom.appendChild(progress);
},
/**
* 滑动条组件创建方法
* dom 滑动条容器
* data 滑动条数据模型
*/
slider:function(dom,data){
// 滑动条拨片
var bar=document.createElement('span'),
// 滑动条进度容器
progress=document.createElement('div'),
// 滑动条容量信息提示
totalText=null,
// 滑动条拨片提示信息
progressText=null,
// 数据模型数据,结构:{ position : 60 , total : 200}
param=data.data,
// 容器元素宽度
width=dom.clientWidth,
// 容器元素横坐标
left=dom.offsetLeft,
// 拨片位置(以模型数据中 position 数据计算)
realWidth=(param.position||100)*width/100;
// 清空滑动条容器,为创建滑动条做准备
dom.innerHTML='';
// 如果模型数据中提供容器总量信息(param.total),则创建滚动条提示文案
if(param.total){
// 容器总量提示文案
var text=document.createElement('b');
// 拨片位置提示文案
progressText=document.createElement('em');
// 设置容器总量提示文案
text.innerHTML=param.total;
// 将容器总量提示文案元素添加到滑动条组件中
dom.appendChild(text);
// 将拨片位置提示文案元素添加到滑动条组件中
dom.appendChild(progressText);
}
// 设置滑动条 ??? 什么东西
setStyle(realWidth);
// 为滑动条组件添加 UI 样式
dom.className+=' ui-slider';
// 将进度条容器添加到滑动条组件中
dom.appendChild(progress);
// 将拨片添加到滑动条组件中
dom.appendChild(bar);
// 设置滑动条
function setStyle(w){
// 设置容器宽度
progress.style.width=w+'px';
// 设置拨片横坐标
bar.style.left=w-FONTSIZE / 2 +'px';
// 如果有拨片提示文案
if(progressText){
// 设置拨片提示文案横坐标
progressText.style.left=w-FONTSIZE / 2 * 2.4 + 'px';
// 设置拨片提示文案内容
progressText.innerHTML=parseFloat(w/width*100).toFixed(2)+'%';
};
}
// 创建组件逻辑
// 按下鼠标拨片
bar.onmousedown=function(){
// 移动拨片( 鼠标光标在页面中滑动,事件绑定给 document 是为了优化加护体验,
// 使鼠标光标在页面中自由滑动 )
document.onmousemove=function(event){
// 获取事件源
var e=event||window.event;
// 鼠标光标相对于滑动条容器位置原点移动的横坐标
var w=e.clientX-left;
// 设置滑动条
setStyle( w < width ? ( w > 0 ? w : 0 ) : width );
}
// 阻止页面滑动选取事件 注:这个事件主要是用于禁止选择网页中的文字
document.onselectstart=function(){
return false;
}
}
// 停止滑动交互 ( 鼠标按键松开 )
document.onmouseup=function(){
// 取消文档鼠标光标移动事件
document.onmousemove=null;
// 取消文档滑动选取事件
document.onselectstart=null;
}
}
};
/***
* 获取视图层组件渲染数据的映射信息
* dom 组件元素
**/
function getBindData(dom){
// 获取组件自定义属性 data-bind 值
var data=dom.getAttribute('data-bind');
// 将自定义属性 data-bind 值转化为对象 ??? !!什么意思
return !!data && (new Function("return ({" + data + "})"))();
}
// 组件实例化方法 40.9
return function(){
// 获取页面中所有元素
var doms=document.body.getElementsByTagName('*'),
// 元素自定义数据句柄
ctx=null;
// ui 处理是会向页面中插入元素,此时 dom.length 会改变,此时动态获取 dom.length
for(var i=0;i<doms.length;i++){
// 获取元素自定义数据 例子:{type:'slider',data:demo2}
ctx=getBindData(doms[i]);
// 如果元素 UI 组件,则根据自定义属性中组件类型,渲染该组件
ctx.type && Method[ctx.type] && Method[ctx.type](doms[i],ctx);
}
}
}();
// 将视图模型绑定在 Window 上,供外部获取
window.VM=VM;
})();
// 数据模型层中获取到的组件渲染数据
// 带有提示文案的滑动条
var demo1={
position:11.159,
total:200
},
// 简易版滑动条
demo2={
position:20
},
// 进度条
demo3={
position:50
};
window.onload=function(){
// 渲染组件
VM();
};
</script>
</body>
</html>
以上 js 部分的代码摘自书《JavaScript 设计模式》中的代码示例。