qq临时会话组件
HTML5是用于Web的复杂编程平台,具有强大的功能,例如canvas
元素,CSS3和事件模型。 HTML5提供了实现自己的组件所需的所有基础功能。
本文继续讨论本系列第一期中开始的滑块组件。 您将看到如何合并事件侦听器,动画化滑块的旋钮以及如何将滑块本身注入到现有的DOM树中。 更具体地说,您将学习如何:
- 与滑块一起使用备用控件
- 实现对注册和触发变更侦听器的支持
- 使用CSS3过渡对HTML元素进行动画处理
- 将内容注入DOM树
- 使用元素属性自定义临时组件
备用滑块控件
图1显示了第1部分中使用滑块组件的简单应用程序的变体。 此变体使用链接而不是按钮来增加和减少滑块的值。 对于这些控件,可以使用任何生成鼠标单击事件的元素。
图1.使用链接而不是按钮
用户还可以通过单击滑条的轨道或拖动其旋钮来更改滑条的值。 第1部分中的“ 拖动旋钮 ”部分说明了滑块如何促进其旋钮的拖动。 在本文的“ 使滑块的旋钮动画化 ”部分,您将看到当用户单击滑块的导轨时,滑块如何通过CSS3过渡来调整旋钮的位置。
清单1显示了图1所示的应用程序HTML。
清单1.带有链接而不是按钮的滑块应用程序HTML
<html>
<head>
...
<head>
<body>
<div id='slider-component'>
<div id='controls'>
<a href='#' class='slider-control' id='decrement'>-</a>
<a href='#' class='slider-control' id='increment'>+</a>
<div id='slider'> <!-- slider goes here --> </div>
</div>
<div id='readout'>0</div>
</div>
</body>
<script type="text/javascript" src="lib/slider.js"></script>
<script type="text/javascript" src="sliderExample.js"></script>
</html>
清单1中HTML很简单。 两个链接和滑块位于controls DIV
,而controls DIV
又位于slider-component DIV
。 当用户单击任一链接时,应用程序JavaScript处理click事件,如清单2所示。
清单2.链接单击事件
var slider = new COREHTML5.Slider('black', 'cornflowerblue', 0),
...
document.getElementById('decrement').onclick = function (e) {
slider.knobPercent -= 0.1;
slider.redraw();
updateReadout();
}
document.getElementById('increment').onclick = function (e) {
slider.knobPercent += 0.1;
slider.redraw();
updateReadout();
}
清单2中的事件处理程序增加和减少滑块的值(存储在滑块的knobPercent
属性中),重新绘制滑块,并更新readout
元素。
当滑块的值更改时,应用程序将使用附加到滑块的更改侦听器更新readout
元素,如清单3所示。
清单3.滑块更改事件
var readoutElement = document.getElementById('readout');
function updateReadout() {
if (readoutElement)
readoutElement.innerHTML = slider.knobPercent.toFixed(2);
}
slider.addChangeListener(updateReadout);
注意,readout元素是可选的; 如果不存在,则updateReadout()
方法不执行任何操作。
下一节将向您展示滑块如何实现对更改侦听器的支持。
图1中所示的应用程序使用滑块的appendTo()
方法将滑块附加到DOM:
slider.appendTo('slider'); // Append the slider to the DOM element with an ID of slider
slider.draw();
本文在“ 将滑块附加到DOM元素 ”部分中讨论了该方法的实现。
支持滑块更改事件
在上一节中,您看到了图1中的应用程序如何通过将更改侦听器附加到滑块来使可选读数保持同步。 清单4显示了滑块如何使开发人员添加更改侦听器,以及如何向这些侦听器触发更改事件。
清单4.添加变更侦听器和触发事件
COREHTML5.Slider = function {
...
this.changeEventListeners = [];
...
};
COREHTML5.Slider.prototype = {
...
addChangeListener: function (listenerFunction) {
this.changeEventListeners.push(listenerFunction);
},
fireChangeEvent: function(e) {
for (var i=0; i < this.changeEventListeners.length; ++i) {
this.changeEventListeners[i](e);
}
},
...
};
每个滑块均包含一系列功能。 当滑块的值更改时, fireChangeEvent()
方法将迭代该数组,依次调用每个函数。 清单5显示了滑块如何调用其fireChangeEvent()
方法。
清单5.触发变更事件
COREHTML5.Slider.prototype = {
addMouseListeners: function () {
this.knobCanvas.addEventListener('mousemove', function(e) {
var mouse = null,
percent = null;
e.preventDefault();
if (slider.draggingKnob) {
slider.deactivateKnobAnimation();
mouse = slider.windowToCanvas(e.clientX, e.clientY);
percent = slider.knobPositionToPercent(mouse.x);
if (percent >= 0 && percent <= 1.0) {
slider.fireChangeEvent(e);
slider.erase();
slider.draw(percent);
}
}
}, false);
},
当用户拖动滑块的旋钮时,连接到旋钮画布的鼠标移动事件侦听器将调整滑块的值,触发更改事件,并擦除并重画滑块。 当用户拖动旋钮时,该事件侦听器还会停用滑块的旋钮动画,以确保动画不会干扰用户拖动旋钮。
既然您已经了解了在什么情况下滑块会停用旋钮动画,那么让我们看一下该动画的工作方式。
动画化滑块的旋钮
图2显示了滑块如何动画化其旋钮。 图2的顶部屏幕截图是在用户单击滑块的导轨之前拍摄的。 最下面的两个屏幕截图显示了旋钮随后移动到光标所在的位置。 (图2是实际动画的近似值;要获得完整的效果,请下载代码并尝试一下。)
图2.动画化滑块
正如我在第1部分“ 创建和初始化滑块 ”一节中提到的那样,滑块在单独的画布元素中绘制其导轨和旋钮。 如果滑块在同一块画布上画出滑轨和旋钮,会更简单; 但是,CSS3过渡仅适用于单个元素,因此旋钮必须位于单独的画布中。
在上一节中,您看到了滑块在用户拖动时调用其deactivateKnobAnimation()
方法来停用旋钮动画的功能。 清单6显示了该方法及其相反的方法activateKnobAnimation()
。
清单6.激活和禁用旋钮动画
COREHTML5.Slider.prototype = {
...
activateKnobAnimation: function () {
var transitionString = "margin-left " +
(this.knobAnimationDuration / 1000).toFixed(1) + "s";
this.knobCanvas.style.webkitTransition = transitionString;
this.knobCanvas.style.MozTransition = transitionString;
this.knobCanvas.style.OTransition = transitionString;
this.knobCanvas.style.transition = transitionString;
},
deactivateKnobAnimation: function () {
slider.knobCanvas.style.webkitTransition = "margin-left 0s";
slider.knobCanvas.style.MozTransition = "margin-left 0s";
slider.knobCanvas.style.OTransition = "margin-left 0s";
slider.knobCanvas.style.transition = "margin-left 0s";
},
...
};
清单6中所示的activateKnobAnimation()
方法以编程方式向旋钮画布元素的margin-left
CSS属性添加了CSS3过渡,考虑了浏览器对过渡属性本身名称的变化。 过渡的结果是,当margin-left
属性更改时,浏览器会平滑地将旋钮画布从一个位置动画化到另一个位置。 动画的持续时间是使用滑块的knobAnimationDuration
属性指定的,以毫秒为单位。
deactivateKnobAnimation()
方法将CSS3过渡的持续时间更改为零秒,从而有效地禁用了动画。
当用户单击滑块的滑轨时,清单7中所示的事件处理程序将滑块的旋钮移至单击位置。
清单7.单击滑块的轨道
COREHTML5.Slider.prototype = {
...
addMouseListeners: function () {
...
this.railCanvas.onmousedown = function(e) {
var mouse = slider.windowToCanvas(e.clientX, e.clientY),
startPercent,
endPercent;
e.preventDefault();
startPercent = slider.knobPercent;
endPercent = slider.knobPositionToPercent(mouse.x);
slider.animatingKnob = true;
slider.moveKnob(mouse.x);
slider.trackKnobAnimation(startPercent, endPercent);
};
},
...
};
清单8中所示的滑块的moveKnob()
方法通过设置旋钮画布元素的margin-left
CSS属性来移动旋钮。 设置该属性将触发CSS3旋钮动画,前提是旋钮动画处于活动状态。
清单8.移动滑块的旋钮
COREHTML5.Slider.prototype = {
...
moveKnob: function (position) {
this.knobCanvas.style.marginLeft = position - this.knobCanvas.width/2 + "px";
},
...
};
除了移动滑块的旋钮之外,铁路画布的mouse-down事件处理程序还调用滑块的trackKnobAnimation()
方法,如清单7所示。 下一部分将讨论该方法,在整个CSS3过渡的相应动画中,使滑块的值与旋钮保持同步。
跟踪CSS3过渡
您可以使用事件侦听器检测CSS3过渡的结束。 滑块组件正是通过从滑块的构造函数中调用滑块的addKnobTransitionListener()
方法来实现此目的的,如清单9所示。
清单9.添加旋钮过渡侦听器
COREHTML5.Slider = function(strokeStyle, fillStyle, knobPercent, knobAnimationDuration) {
...
this.createDOMTree();
this.addMouseListeners();
this.addKnobTransitionListener();
};
清单10中所示的滑块的addKnobTransitionListener()
方法将转换侦听器添加到旋钮画布,再次考虑了浏览器之间的命名差异。
清单10. CSS过渡侦听器
COREHTML5.Slider.prototype = {
...
addKnobTransitionListener: function () {
var BROWSER_PREFIXES = [ 'webkit', 'o' ];
for (var i=0; i < BROWSER_PREFIXES.length; ++i) {
this.knobCanvas.addEventListener(
BROWSER_PREFIXES[0] + "TransitionEnd", // Everything but Mozilla
function (e) {
slider.animatingKnob = false;
}
);
}
this.knobCanvas.addEventListener("transitionend", // Mozilla
function (e) {
slider.animatingKnob = false;
}
);
},
...
旋钮过渡的动画完成后,浏览器将调用清单10中的过渡侦听器。 该侦听器只是将滑块的animatingKnob
属性设置为false
,这将导致滑块停止跟踪动画旋钮。 跟踪是通过滑块的trackKnobAnimation()
方法实现的,如清单11所示。
清单11.跟踪旋钮动画
trackKnobAnimation: function (startPercent, endPercent) {
var count = 0,
KNOB_ANIMATION_FRAME_RATE = 60, // fps
iterations = slider.knobAnimationDuration/1000 * KNOB_ANIMATION_FRAME_RATE + 1,
interval;
interval = setInterval( function (e) {
if (slider.animatingKnob) {
slider.knobPercent = startPercent +
((endPercent - startPercent) / iterations * count++);
slider.knobPercent = slider.knobPercent > 1.0 ? 1.0 : slider.knobPercent;
slider.knobPercent = slider.knobPercent < 0 ? 0 : slider.knobPercent;
slider.fireChangeEvent(e);
}
else { // Done animating knob
clearInterval(interval);
count = 0;
}
}, slider.knobAnimationDuration / iterations);
},
...
};
尽管可以使用事件侦听器检测CSS3过渡动画的结束,如清单10所示 ,但是您无法检测到该动画的中间步骤,这对于滑块在触发动画时触发更改事件是必需的。
由于无法检测CSS3过渡的动画的步骤,因此滑块的trackKnobAnimation()
方法使用setInterval()
来逼近动画的步骤并在每个步骤触发更改事件。
现在,您已经了解了如何使用CSS3过渡为滑块的旋钮设置动画,让我们看看如何将滑块附加到现有DOM元素上。
将滑块附加到DOM元素
第1部分中的“ 绘制滑块 ”部分讨论了滑块组件的实现,而没有参考CSS3过渡。 该实现产生了滑块的DOM树,如图3所示。
图3.滑块的DOM树
滑块的构造函数创建一个DIV
和两个canvas
元素(每个用于滑块的导轨及其旋钮),并将画布附加到DIV
。 清单1中所示的应用程序随后将滑块添加到现有DOM元素时,滑块将滑块的封闭DOM元素添加到现有元素,如图4所示。
图4.合并滑块的DOM树
滑块的构造函数调用滑块的createDOMTree()
方法,如清单12所示。
清单12.创建滑块的DOM树
COREHTML5.Slider = function(strokeStyle, fillStyle, knobPercent, knobAnimationDuration) {
...
this.createDOMTree();
this.addMouseListeners();
this.addKnobTransitionListener();
};
清单13显示了滑块的createDOMTree()
方法。
清单13. createDOMTree()
方法
COREHTML5.Slider.prototype = {
...
createDOMTree: function () {
var self = this;
this.domElement = document.createElement('div');
this.domElement.appendChild(this.knobCanvas);
this.domElement.appendChild(this.railCanvas);
},
...
};
到滑块调用createDOMTree()
,它已经为旋钮和导轨创建了画布。 createDOMTree()
方法创建滑块的封闭DIV
元素,并将现有的旋钮和滑轨画布添加到该元素。
在滑块创建了其DOM树之后,设置阶段,以使用滑块的appendTo()
方法将滑块附加到现有DOM元素,如清单14所示。
清单14. appendTo()
方法:将滑块附加到现有的DOM元素
COREHTML5.Slider.prototype = {
...
appendTo: function (elementName) {
if (typeof element === 'string') {
document.getElementById(element).
appendChild(this.domElement);
}
else {
element.appendChild(this.domElement);
}
this.resize();
},
...
};
设置CSS大小和画布元素大小
清单15中滑块的setKnobCanvasSize()
方法的最后四行说明了一个重点: 在不将canvas元素的width和height属性设置为相同值的情况下,不应为canvas元素设置CSS width和height属性 。 这是因为CSS属性仅适用于canvas元素,而canvas width和height属性同时适用于canvas元素及其绘图表面。 如果仅设置CSS属性,则画布元素的大小与其绘图表面的大小会发生不匹配,从而导致浏览器缩放画布的绘图表面以适合该元素。 如清单15所示,滑块的resize()
方法除了设置滑块本身的大小外,还设置滑块的滑轨和旋钮画布的大小。
您可以将代表元素ID的字符串或元素本身传递给appendTo()
方法。 无论哪种方式,该方法都会将滑块的封闭DOM元素附加到指定的元素,并调整画布和滑块的封闭DOM元素的大小。 调整大小发生在resize()
方法及其调用的方法中,所有这些如清单15所示。
清单15.调整滑块的大小以适合其封闭元素
COREHTML5.Slider.prototype = {
...
setRailCanvasSize: function () {
var domElementParent = this.domElement.parentNode;
this.railCanvas.width = domElementParent.offsetWidth;
this.railCanvas.height = domElementParent.offsetHeight;
},
setKnobCanvasSize: function () {
this.knobRadius = this.railCanvas.height/2 -
this.railContext.lineWidth;
this.knobCanvas.style.width = this.knobRadius * 2 + "px";
this.knobCanvas.style.height = this.knobRadius * 2 + "px";
this.knobCanvas.width = this.knobRadius*2;
this.knobCanvas.height = this.knobRadius*2;
},
setSliderSize: function() {
this.cornerRadius = (this.railCanvas.height/2 -
2*this.VERTICAL_MARGIN)/2;
this.top = this.HORIZONTAL_MARGIN;
this.left = this.VERTICAL_MARGIN;
this.right = this.left +
this.railCanvas.width - 2*this.HORIZONTAL_MARGIN;
this.bottom = this.top +
this.railCanvas.height - 2*this.VERTICAL_MARGIN;
},
resize: function() {
this.setRailCanvasSize();
this.setKnobCanvasSize();
this.setSliderSize();
},
...
};
定制组件
当前存在的滑块组件非常有用。 但是,不管组件提供多少实用程序,人们都希望以某种方式对其进行自定义很有可能。 这些自定义可以像更改颜色一样简单,如图5所示,也可以像支持垂直滑块一样实质性(本文中讨论的滑块不支持)。
图5.浅绿色滑块
清单16显示了向滑块组件DIV
元素添加两个属性,用于滑块的stroke
和fill
颜色。
清单16.指定属性元素
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<div id='title'>A custom slider</div>
<p>
<div id='slider-component' stroke='blue' fill='aqua'>
...
</div>
</p>
</body>
<script type="text/javascript" src="lib/slider.js"></script>
<script type="text/javascript" src="sliderExample.js"></script>
</html>
该应用程序JavaScript使用getAttribute()
方法(如清单17所示getAttribute()
来获取stroke
和fill
属性的值。
清单17.访问元素属性
var sliderElement = document.getElementById('slider-component'),
slider = new COREHTML5.Slider(sliderElement.getAttribute('stroke'),
sliderElement.getAttribute('fill'),
0),
...
随后,它将使用这些值来创建滑块。
下次
本系列的下一篇文章将讨论W3C的“ Web组件简介”规范,并向您展示如何使用Shadow DOM,自定义元素和模板来实现滑块组件。 下次见。
翻译自: https://www.ibm.com/developerworks/web/library/wa-html5components2/index.html
qq临时会话组件