我目前正在为在线发布商开发第三方小部件,并希望跟踪其在不同网站上的效果。我想过实现Upworthy的代码(http://upworthy.github.io/2014/06/implementing-attention-minutes-part-1/)来计算我的小部件上每个用户的平均时间。所以我决定分三个步骤实现它:
我已经实现了上述内容,源代码可以在这里找到:http://jsfiddle.net/q21gzjmf/13/
//simple jquery function to detect when my widget is visible
function widgetFocus(){
$(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() > $('.footer').offset().top) {
alert('visible');
return true;
}
else{
return false;
}
});
}
function merge(stream1, stream2) {
return stream1.merge(stream2);
}
function eventStream(eventName) {
return $(window).asEventStream(eventName);
}
var isFocused = eventStream("focus").map(widgetFocus)
.merge(eventStream("blur").map(false))
.toProperty(true);
var EVENT_NAMES = ["focus", "click", "scroll", "mousemove", "touchstart", "touchend", "touchcancel", "touchleave", "touchmove"];
var streams = _.map(EVENT_NAMES, eventStream);
var interactionStream = _.reduce(streams, merge);
var recentlyActive = interactionStream
.map(true)
.flatMapLatest(function() {
return Bacon.once(true).merge(Bacon.once(false).delay(DECAY));
})
.toProperty(false);
var isActive = (recentlyActive.and(isFocused));
var secondsActive = Bacon.mergeAll(isActive.changes(), isActive.sample(1000))
.map(function(isActive) {
return {
isActive: isActive,
timestamp: new Date().getTime()
};
})
.slidingWindow(2,2)
.filter(function(span) { return span[0].isActive; })
.map(function(span) { return span[1].timestamp - span[0].timestamp; })
.scan(0, function(x,y) { return x + y;})
.map(function(x) { return x / 1000; }) // milliseconds
.map(Math.floor);
secondsActive.assign($("#seconds"), "text");
但是,如果向下滚动到底部,您会发现花费的时间计算为0并且不会动态更新,这与此处显示的Upworthy实现http://jsfiddle.net/zanes/mbGBr/不同。 我对功能反应式编程的概念很新,并且仍然试图了解Bacon.JS,所以我确信我必须犯了一个非常愚蠢的概念错误但我在这里要学习。任何帮助将不胜感激。
答案 0 :(得分:2)
重复步骤在窗口小部件可见时启动焦点事件:
为此我们需要一个谓词来返回是否可见小部件:
function widgetFocus(){
return $(window).scrollTop() + $(window).height() > $('.footer').offset().top;
}
然后我们继续处理所有与滚动相关的事件:
function eventStream(eventName) {
return $(window).asEventStream(eventName);
}
var EVENT_NAMES = ["focus", "click", "scroll", "mousemove", "touchstart", "touchend", "touchcancel", "touchleave", "touchmove"];
var streams = _.map(EVENT_NAMES, eventStream);
var scrollEvents = Bacon.mergeAll(streams)
.debounceImmediate(50); // we debounce here, so we didn't get too much events
接下来,您可以映射scrollEvents以获取布尔流,以指示是否显示小部件。
var isVisible$ = scrollEvents
.map(function () {
return widgetFocus();
})
.toProperty(false);
var secondsVisible = isVisible$.changes()
.map(function(isActive) {
return {
isActive: isActive,
timestamp: new Date().getTime()
};
})
.slidingWindow(2,2)
.filter(function(span) { return span[0].isActive; })
.map(function(span) { return span[1].timestamp - span[0].timestamp; })
.scan(0, function(x,y) { return x + y;})
.map(function(x) { return Math.floor(x / 1000); }); // milliseconds
secondsVisible.log("visible: ");
这是第一步,可能已经适合你了。
下一步是创建一个属性或流,以确定页面是否处于活动状态(简单版本的Upworthy):
var DECAY = 1000;
function decayingStream() {
return Bacon.once(true).merge(Bacon.once(false).delay(DECAY));
}
var isActive$ = scrollEvents
.flatMapLatest(decayingStream)
.toProperty(true);
接下来,我们可以合并isActive$
和isVisible$
属性。我们只对时间窗口小部件可见且页面处于活动状态感兴趣!
var secondsVisibleAndActive = isActive$.and(isVisible$).changes()
.map(function(isActive) {
return {
isActive: isActive,
timestamp: new Date().getTime()
};
})
.slidingWindow(2,2)
.filter(function(span) { return span[0].isActive; })
.map(function(span) { return span[1].timestamp - span[0].timestamp; })
.scan(0, function(x,y) { return x + y;})
.map(function(x) { return Math.floor(x / 1000); }); // milliseconds
secondsVisibleAndActive.log("active & visible: ");
secondsVisibleAndActive.assign($("#seconds"), "text");
与往常一样,从简单开始。
或者你可以举例说明Upworthy的实现之美:最后一行:
var hasAttention = (recentlyActive.and(isFocused)).or(videoIsPlaying);
在您的情况下,您可以将其更改为:
var widgetHasAttention = recentlyActive.and(isFocused).and(widgetIsVisible);
工作jsfiddle:http://jsfiddle.net/xkn3sc6y/3/