Authored by 王洪广

添加奥莱导航,倒计时相关

/**
* 频道页面
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const library = '../../../library';
const _ = require('lodash');
const outletModel = require('../models/outlet');
const helpers = require(`${library}/helpers`);
const log = require(`${library}/logger`);
const renderData = {
module: 'outlet',
page: 'home'
};
const outletLogger = (err, res) => {
log.error('奥莱页面渲染错误:' + JSON.stringify(err));
res.send('error');
};
const getNavData = (req, res) => {
outletModel.getNavData().then(result => {
console.log(result)
res.render('outlet', Object.assign({}, renderData, result));
})
}
exports.index = (req, res) => {
getNavData(req, res)
}
\ No newline at end of file
... ...
/**
* sub app channel
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: require(`${global.library}/helpers`)
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* 频道页面 model
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const library = '../../../library';
const utils = '../../../utils';
const _ = require('lodash');
const ServiceAPI = require(`${library}/api`).ServiceAPI;
const sign = require(`${library}/sign`);
const camelCase = require(`${library}/camel-case`);
const api = new ServiceAPI();
exports.getNavData = () => {
return api.get('3b8b45b8d87c8e44.json').then( result => {
if(result && result.code === 200){
return result.data;
}else{
return result
}
});
}
... ...
/**
* router of sub app channel
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const express = require('express');
const cRoot = './controllers';
const outlet = require(cRoot);
const router = express.Router(); // eslint-disable-line
router.get('/index',outlet.index)
module.exports = router;
... ...
{{> outlet/nav }}
{{> outlet/countdown }}
\ No newline at end of file
... ...
<div id="demo1">
<p>1 默认,服务端输出leftTime,把客户端时间干扰降到最低。单位秒</p>
<div class="cd cd-lite" data-config="{'leftTime':10}">剩余时间:${d}天${h}时${m}分${s}秒</div>
<div class="cd cd-medium" data-config="{'leftTime':10}">剩余时间:${h}时${m}分${s}秒</div>
</div>
<div id="demo4">
<p>2 notify:cd.notify(xx, fn); 当倒计时还剩xx秒时调用fn,精准度为 1s</p>
<div class="cd cd-large cd-slide" data-config="{'leftTime':4}">剩余时间:<span class="clock">${h}时${m}分${s}秒</span></div>
</div>
<div id="demo6">
<p>3 使用本地时间(注意:stopPoint为UNIX时间戳,故单位是毫秒) & 使用js配置,不依赖DOM上的data-config</p>
<div class="cd cd-large cd-slide" data-config="{'stopPoint':4529059200000,'effect':'slide'}">剩余时间:<span class="clock">${d}天${h}时${m}分${s}秒</span></div>
</div>
\ No newline at end of file
... ...
<nav class="outlet-nav">
<ul>
{{#each nav}}
<li><a href="{{url}}">{{name}}</a></li>
{{/each}}
</ul>
</nav>
\ No newline at end of file
... ...
... ... @@ -13,4 +13,6 @@ module.exports = app => {
// 业务模块
app.use('/product', require('./apps/product'));
app.use('/outlet',require('./apps/outlet'));
};
... ...
/**
* countdown.js.
* @author hgwang
* @date 2016-05-29
*/
'use strict';
var $ = require("yoho-jquery");
var EVENT_AFTER_PAINT = 'afterPaint';
var defaultOPtions = {
el: {},
// unix时间戳,单位应该是毫秒!
stopPoint: 0,
leftTime: 0,
template: '', //'${h}时${m}分${s-ext}秒'
varRegular: /\$\{([\-\w]+)\}/g,
clock: ['d', 100, 2, 'h', 24, 2, 'm', 60, 2, 's', 60, 2, 'u', 10, 1],
effect: 'normal'
}
var effect = {
normal: {
paint: function () {
var me = this,
content;
// 找到值发生改变的hand
$.each(me.hands, function (index, hand) {
if (hand.lastValue !== hand.value) {
// 生成新的markup
content = '';
$.each(me._toDigitals(hand.value, hand.bits), function (index, digital) {
content += me._html(digital, '', 'digital');
});
// 并更新
hand.node.html(content);
}
});
}
}
}
var timer = (function () {
var fns = [],
commands = [];// 操作指令
/**
* timer
* 调用频率为100ms一次。努力精确计时,调用帧函数
*/
function timer() {
// 为避免循环时受到 对fns数组操作 的影响,
// add/remove指令提前统一处理
while (commands.length) {
commands.shift()();
}
// 计算新时间,调整diff
var diff = +new Date() - timer.nextTime,
count = 1 + Math.floor(diff / 100);
diff = 100 - diff % 100;
timer.nextTime += 100 * count;
// 循环处理fns二元组
var frequency, step,
i, len;
for (i = 0, len = fns.length; i < len; i += 2) {
frequency = fns[i + 1];
// 100次/s的
if (0 === frequency) {
fns[i](count);
// 1000次/s的
} else {
// 先把末位至0,再每次加2
frequency += 2 * count - 1;
step = Math.floor(frequency / 20);
if (step > 0) {
fns[i](step);
}
// 把末位还原成1
fns[i + 1] = frequency % 20 + 1;
}
}
// next
setTimeout(timer, diff);
}
// 首次调用
timer.nextTime = +new Date();
timer();
function indexOf(item, arr) {
for (var i = 0, len = arr.length; i < len; ++i) {
if (arr[i] === item) {
return i;
}
}
return -1;
}
return {
add: function (fn, frequency) {
commands.push(function () {
fns.push(fn);
fns.push(frequency === 1000 ? 1 : 0);
});
},
remove: function (fn) {
commands.push(function () {
var i = indexOf(fn, fns);
if (i !== -1) {
fns.splice(indexOf(fn, fns), 2);
}
});
}
};
})();
function Countdown(config) {
if (!(this instanceof Countdown)) {
return new Countdown(config);
}
config.el = $(config.el);
if (!config.el) return;
var cfg = config.el.attr('data-config');
if (cfg) {
cfg = JSON.parse(cfg.replace(/'/g, '"'));
config = $.extend(true, {}, defaultOPtions, cfg, config);
}
this.config = config;
this._init();
}
$.extend(Countdown.prototype, {
/**
* 初始化
* @private
*/
_init: function () {
var me = this;
var el = me.config.el;
// 初始化时钟.
var hands = [];
/**
* 指针结构
* hand: {
* type: string,
* value: number,
* lastValue: number,
* base: number,
* radix: number,
* bits: number,
* node: S.Node
* }
*/
me.hands = hands;
me.frequency = 1000;
me._notify = [];
// 分析markup
var tmpl = el.html();
var varRE = me.config.varRegular;
varRE.lastIndex = 0;
el.html(tmpl.replace(varRE, function (str, type) {
// 时钟频率校正.
if (type === 'u' || type === 's-ext') {
me.frequency = 100;
}
// 生成hand的markup
var content = '';
if (type === 's-ext') {
hands.push({type: 's'});
hands.push({type: 'u'});
content = me._html('', 's', 'handlet') +
me._html('.', '', 'digital') +
me._html('', 'u', 'handlet');
} else {
hands.push({type: type});
}
return me._html(content, type, 'hand');
}));
// 指针type以外属性(node, radix, etc.)的初始化.
var clock = me.config.clock;
$.each(hands, function (index, hand) {
var type = hand.type,
base = 100, i;
hand.node = el.find('.hand-' + type);
// radix, bits 初始化.
for (i = clock.length - 3; i > -1; i -= 3) {
if (type === clock[i]) {
break;
}
base *= clock[i + 1];
}
hand.base = base;
hand.radix = clock[i + 1];
hand.bits = clock[i + 2];
});
me._getLeft();
me._reflow();
// bind reflow to me.
var _reflow = me._reflow;
me._reflow = function () {
return _reflow.apply(me, arguments);
};
timer.add(me._reflow, me.frequency);
// 显示时钟.
el.show();
},
/**
* 获取倒计时剩余帧数
*/
_getLeft: function () {//{{{
var left = this.config.leftTime * 1000;
var end = this.config.stopPoint; // 这个是UNIX时间戳,毫秒级
if (!left && end) {
left = end - (+new Date());
}
this.left = left - left % this.frequency;
},//}}}
/**
* 更新时钟
*/
_reflow: function (count) {
count = count || 0;
var me = this;
me.left = me.left - me.frequency * count;
// 更新hands
$.each(me.hands, function (index, hand) {
hand.lastValue = hand.value;
hand.value = Math.floor(me.left / hand.base) % hand.radix;
});
// 更新时钟.
me._repaint();
// notify
if (me._notify[me.left]) {
$.each(me._notify[me.left], function (index, callback) {
callback.call(me);
});
}
// notify 可能更新me.left
if (me.left < 1) {
timer.remove(me._reflow);
}
return me;
},
/**
* 重绘时钟
* @private
*/
_repaint: function () {
effect[this.config.effect].paint.apply(this);
this.config.el.trigger(EVENT_AFTER_PAINT);
},
/**
* 把值转换为独立的数字形式
* @private
* @param {number} value
* @param {number} bits
*/
_toDigitals: function (value, bits) {
value = value < 0 ? 0 : value;
var digitals = [];
// 把时、分、秒等换算成数字.
while (bits--) {
digitals[bits] = value % 10;
value = Math.floor(value / 10);
}
return digitals;
},
/**
* 生成需要的html代码,辅助工具
* @private
* @param {string|Array.<string>} content
* @param {string} className
* @param {string} type
*/
_html: function (content, className, type) {
if ($.isArray(content)) {
content = content.join('');
}
switch (type) {
case 'hand':
className = type + ' hand-' + className;
break;
case 'handlet':
className = type + ' hand-' + className;
break;
case 'digital':
if (content === '.') {
className = type + ' ' + type + '-point ' + className;
} else {
className = type + ' ' + type + '-' + content + ' ' + className;
}
break;
}
return '<i class="' + className + '">' + content + '</i>';
},
/**
* 倒计时事件
* @param {number} time unit: second
* @param {Function} callback
*/
notify: function (time, callback) {
time = time * 1000;
time = time - time % this.frequency;
var notifies = this._notify[time] || [];
notifies.push(callback);
this._notify[time] = notifies;
return this;
}
});
exports.Countdown = Countdown;
... ...
var $ = require("yoho-jquery"),
IScroll = require("yoho-iscroll"),
Countdown = require('./countdown').Countdown;
// nav 滚动
function initNavScroll() {
var $navBox = $(".outlet-nav"),
$lis = $navBox.find("li"),
iScroll;
iScroll = new IScroll($navBox[0], {
scrollX: true,
scrollY: false,
});
$navBox.on("click", "li", function() {
var $this = $(this);
var i = $this.index();
$lis.eq(i).addClass("active").siblings().removeClass("active");
scroll($this[0]);
});
function scroll(ele) {
var offset = -($navBox.find("ul").width() - 20) / 2;
setTimeout(function() {
iScroll.scrollToElement(ele, 400, offset)
}, 1);
}
//scroll($navBox.find(".active")[0]);
}
function initCountDown(){
$('#demo1 .cd').each(function(index,node) {
Countdown({
el: node,
callback:function(){
alert(1)
}
});
});
var cd = Countdown({
el: '#demo4 .cd'
});
cd.notify(0, function() {
alert(1)
});
Countdown({
el: '#demo6 .cd',
stopPoint: 1464515700000,
effect: 'normal'
});
}
initNavScroll();
initCountDown();
\ No newline at end of file
... ...
require('./home');
\ No newline at end of file
... ...
... ... @@ -18,3 +18,4 @@
@import "product/filter";
@import "product/suspend-cart";
@import "cart/chose-panel";
@import "outlet/index";
\ No newline at end of file
... ...
@import "outlet-nav";
\ No newline at end of file
... ...
.outlet-nav {
width:100%;
height: 88px;
border-top:1px solid #e0e0e0;
border-bottom:1px solid #e0e0e0;
white-space: nowrap;
position: relative;
overflow: hidden;
ul{
padding:0 20px;
margin: 0;
display: table;
table-layout: fixed;
white-space: nowrap;
width: auto;
}
li{
-webkit-box-flex: 1;
display: inline-block;
-webkit-box-align: center;
-webkit-box-pack: center;
vertical-align: top;
margin-right: 10px;
height: 88px;
a{
display: inline-block;
width: auto;
height: 86px;
padding:0 20px;
line-height: 88px;
color: #b0b0b0;
font-size: 28px;
border-bottom: 6px solid #fff;
}
&.active{
a{
border-color: #3d3d3d;
color: #444;
}
}
&:last-child{
margin-right: 0;
}
}
}
\ No newline at end of file
... ...