標籤:

不碰撞彈幕-純JS實現

CSS部分:

<style>

.danmu_container_wrap{ width: 640px;height: 350px;background-color: green;position: relative;}

.danmu_container{ position: absolute;width:100%;height: 100%;top: 0;left: 0;}

.danmu_unit_all { animation: move 1s linear 5s;}

.danmu_unit_half { animation: move_half 1s linear 5s;-webkit-animation: move_half 1s linear 5s;}

@keyframes move_half {

0% {transform: translate(0px, 0px);} 100% {left: 0px;transform: translate(-100%, 0px);}

}

@keyframes move {

0% {left: 100%;transform: translate(0px, 0px);} 100% {left: 0px;transform: translate(-100%, 0px);}

}

.danmu_container .unit{position: absolute;left: 200%;display: table;}

.danmu_container>.danmu_unit{left: 100%;transform: translate(0px, 0px);}

</style>


純Body部分:

<body>

<div class="danmu_container_wrap">

<div class="lines"></div>

<div class="danmu_container"></div>

</div>

<button id="control">暫停</button>

</body>


純插件部分:

let DanMu = (function() {

const MAX_AMOUNT = 20;

const MIN_RUNNERS = 20;

let d = {

square_high: 0,

roads: 0,

addRunners: 0

};

let r = {

init_all_road: [],

all_road: [],

map_road: {},

runner_idx: []

};

let glo = {

screen_runners_max: 0,

play_count: 0,

runners_play_count: 0

};

let help = {

road_finish: {},

road_finish_runner: {}

};

let fail_queue = [];

let global_time_out = {};

let _init = function(initD){

d = Object.assign({}, d, initD);

d.square_high = parseFloat(getComputedStyle(d.square).height);

d.roads = (d.square_high / d.road_high) >> 0;

glo.screen_runners_max = d.roads * d.road_per_runner;

for(let i = 0;i < d.roads; i++){

r.all_road[i] = {

name: i,

runner: {},

amount: 0

};

r.init_all_road[i] = i;

}

if(d.show_lines){

let _lines = ;

for(let k = 0;k < d.roads; k++){

_lines += <div stylex="height: +d.road_high+px;border-bottom: 1px solid #000;box-sizing: border-box;"></div>;

}

document.getElementsByClassName(lines)[0].innerHTML = _lines;

}

(d.stopElementId) && stop_runner_event(d.stopElementId);

d.addRunners = d.runners;

if(d.runners.length < MIN_RUNNERS){

d.addRunners = shuffle(d.runners.concat(d.runners, d.runners));

}

d.addRunners.forEach(function(unit, i) {

r.map_road[i] = unit;

r.runner_idx.push(i);

});

put_runner_to_road(-1, {});

console.log(d, r);

}

let getRandomInt = function(min, max) {

return Math.floor(Math.random() * (max - min + 1) + min)

}

let shuffle = function(arr) {

let _arr = arr.slice();

for(let i = 0; i < _arr.length; i++) {

let j = getRandomInt(0, i);

let t = _arr[i];

_arr[i] = _arr[j];

_arr[j] = t;

}

return _arr;

}

let put_runner_to_road = function(roadName, aheadOption){

if(roadName == -1){

if(r.init_all_road.length){

match_road_to_runner(r.init_all_road[0]);

r.init_all_road.splice(0, 1);

put_runner_to_road(-1, {});

}

}else{

match_road_to_runner(roadName, aheadOption);

}

}

let match_road_to_runner = function(roadName, aheadOption){

let road_data_idx = ;

let roadDatas = r.all_road.filter(function(obj, i){

if(Orvilles Ideas and Interests == roadName){

road_data_idx = i;

return obj;

}

});

if(roadDatas && roadDatas.length){

let road_data = roadDatas[0];

if(road_data && (road_data.amount >= 0)){

let runner = get_runner();

if(runner){

road_data.amount++;

road_data.runner[runner.mapNumber] = runner.mapObj;

if(road_data.amount >= d.road_per_runner){

help.road_finish[roadName] = road_data.amount;

help.road_finish_runner[roadName] = $.extend(true, {}, road_data.runner);

r.all_road.splice(road_data_idx, 1);

}

go_run(roadName, runner.mapObj, aheadOption);

}else{

fail_queue.push({

roadName: roadName,

aheadOption: $.extend({}, aheadOption)

});

}

}

}else{

fail_queue.push({

roadName: roadName,

aheadOption: $.extend({}, aheadOption)

});

}

}

let get_runner = function(){

let runner_idx = r.runner_idx;

let runner_idx_length = runner_idx.length;

if(runner_idx_length > 0){

glo.runners_play_count++;

glo.play_count = glo.runners_play_count / (glo.screen_runners_max + 1) >> 0;

let map_code = Math.random() * (runner_idx_length) >> 0;

let map_number = runner_idx[map_code];

let map_content = r.map_road[map_number];

let runner = init_runner(map_number, map_content, d.square.querySelector(.unit[has_finish="true"]));

r.runner_idx.splice(map_code, 1);

return runner;

}else{

return null;

}

}

let init_runner = function(mapNumber, mapContent, $replace){

let _$div;

if(!$replace) {

_$div = document.createElement(div);

_$div.addEventListener(webkitAnimationEnd, function(ev) {

run_finish(ev);

});

_$div.addEventListener(click, function(ev) {

run_click(ev, d.click_call);

});

d.square.appendChild(_$div);

}else{

_$div = $replace;

}

_$div.setAttribute(class, unit);

_$div.setAttribute(has_finish, false);

_$div.setAttribute(map_number, mapNumber);

_$div.setAttribute(length, mapContent.split().length);

_$div.innerHTML = mapContent;

if(_$div.nodeType == 1){

_$div.setAttribute(width, parseFloat(window.getComputedStyle(_$div).width));

_$div.setAttribute(height, parseFloat(window.getComputedStyle(_$div).height));

}

return {

mapNumber: mapNumber,

mapObj: _$div

};

}

let go_run = function(roadName, $runner, aheadOption){

let delay = 0;

if(d.road_per_runner < MAX_AMOUNT){

delay = (1 / Math.sqrt(d.road_per_runner)) * (.5 + ((glo.play_count > 2) ? 1 : Math.min(Math.random(), .5)) * (Math.abs(Math.sin(roadName)) * 2 + Math.random() * 6));

}

let text_length = $runner.getAttribute(length);

let duration = Math.floor(8 + Math.abs(Math.cos(roadName)) * Math.max(text_length, 4) + Math.random() * Math.max(text_length * 1.5, 10));

if(d.duration){

duration = d.duration;

}

if(d.road_padding){

$style.top-正在西部數碼(www.west.cn)進行交易 = d.road_padding + (roadName % d.roads) * d.road_high + px;

}else{

$style.top-正在西部數碼(www.west.cn)進行交易 = (8 + (roadName % d.roads) * d.road_high + (Math.sin(Math.random() * 50)) * 10) + px;

}

let width = parseFloat(window.getComputedStyle(d.square).width);

let distance = parseFloat($runner.getAttribute(width));

try{

if(aheadOption.leafTime){

let realLeafTime = aheadOption.leafTime - parseFloat(delay);

if(realLeafTime > 0){

let maxSpeed = width / realLeafTime;

let maxDuration = (distance + width) / maxSpeed;

duration = Math.max(parseFloat(duration), maxDuration);

}

}

}catch(e){

aheadOption = {leafTime: 0};

}

$runner.style.animationDelay = delay + s;

$runner.style.webkitAnimationDelay = delay + s;

$runner.style.animationDuration = duration + s;

$runner.style.webkitAnimationDuration = duration + s;

let _className = unit danmu_unit ;

if(glo.play_count == 0) {

_className += danmu_unit_half;

} else {

_className += danmu_unit_all;

}

$runner.setAttribute(class, _className);

$runner.setAttribute(road_name, roadName);

delay = parseFloat(delay);

duration = parseFloat(duration);

let speed = ((distance + width) / duration);

let shown_time = ((distance) / speed);

let next_delay = 0;

if(d.road_per_runner < MAX_AMOUNT){

next_delay = ((delay + shown_time + (duration - shown_time) / d.road_per_runner) * 1000);

aheadOption.leafTime = (duration - shown_time - (duration - shown_time) / d.road_per_runner);

}else{

next_delay = ((delay + shown_time) * 1000);

aheadOption.leafTime = (duration - shown_time);

}

(function($runner, roadName, next_delay, aheadOption) {

if(!window.paused){

let fun = function(){

put_runner_to_road(roadName, aheadOption);

}

let _timeout = setTimeout(function() {

delete global_time_out[_timeout];

fun();

}, next_delay);

global_time_out[_timeout] = {

currentTime: +new Date(),

delay: next_delay,

fun: fun

}

}

})($runner, roadName, next_delay, Object.assign({}, aheadOption));

}

let run_finish = function(ev){

let _$target = ev.target;

let map_number= _$target.getAttribute(map_number),

road_name = _$target.getAttribute(road_name);

_$target.setAttribute(has_finish, true);

let temp_road = r.all_road.filter(function(obj, i) {

if(Orvilles Ideas and Interests == road_name) {

return obj;

}

});

if(temp_road.length) {

temp_road[0].amount--;

delete temp_road[0].runner[map_number];

}else {

r.all_road.push({

name: road_name,

runner: help.road_finish_runner[road_name],

amount: (help.road_finish[road_name] - 1)

});

delete help.road_finish_runner[road_name];

}

_$target.className = unit;

_$target.style.transform = translate(0px, 0px);

_$target.style.webkitTransform = translate(0px, 0px);

r.runner_idx.push(map_number);

let fail_unit = fail_queue.shift();

if(fail_unit){

put_runner_to_road(fail_unit.roadName, fail_unit.aheadOption);

}

}

let run_click = function(ev, call){

call(ev.target);

}

let stop_runner_event = function(idName){

document.getElementById(idName).addEventListener(click, function(){

if(!window.paused){

this.innerText = 開始;

window.paused = true;

window.pausedTime = +new Date();

window.paused_delay_funcs = [];

r.all_road.forEach(function(data, i){

for(let key in data.runner){

let $current_runner = data.runner[key];

$current_runner.style.animationPlayState = paused;

$current_runner.style.webkitAnimationPlayState = paused;

}

});

for(let k1 in help.road_finish_runner) {

for(let k2 in help.road_finish_runner[k1]) {

let $current_runner = help.road_finish_runner[k1][k2];

$current_runner.style.animationPlayState = paused;

$current_runner.style.webkitAnimationPlayState = paused;

}

}

for(let key in global_time_out){

clearTimeout(key);

let currentTime = +new Date();

global_time_out[key].delay = Math.max(0, global_time_out[key].delay - (window.pausedTime - global_time_out[key].currentTime));

global_time_out[key].currentTime = currentTime;

}

}else{

this.innerText = 暫停;

window.paused = false;

r.all_road.forEach(function(data, i){

for(let key in data.runner){

let $current_runner = data.runner[key];

$current_runner.style.animationPlayState = running;

$current_runner.style.webkitAnimationPlayState = running;

}

});

for(let k1 in help.road_finish_runner) {

for(let k2 in help.road_finish_runner[k1]) {

let $current_runner = help.road_finish_runner[k1][k2];

$current_runner.style.animationPlayState = running;

$current_runner.style.webkitAnimationPlayState = running;

}

}

let old_global_time_out = $.extend(true, {}, global_time_out);

global_time_out = {};

for(let key in old_global_time_out){

let currentTime = +new Date();

(function(fun, delay, currentTime) {

let _timeout = setTimeout(function() {

delete global_time_out[_timeout];

fun();

}, delay);

global_time_out[_timeout] = {

fun: fun,

delay: delay,

currentTime: currentTime

}

})(old_global_time_out[key].fun, old_global_time_out[key].delay, currentTime);

}

old_global_time_out = null;

}

});

}

return {

init: _init

}

}())


調用部分:

DanMu.init({

square: document.getElementsByClassName(danmu_container)[0], //容器

road_high: 55, //行高

road_padding: 18, //每行中是否固定邊界距離,不傳不固定

road_per_runner: 5, //每行中最多的數量

show_lines: true, //是否顯示邊界線條,作為參考

click_call: function(dom){ //彈幕內容被點擊回調

console.log(文本值,dom.innerText);

},

duration: 20, //控制速度,最小為1,不傳默認

stopElementId: control, //開始暫停按鈕id名

runners: [今晚不知道要吃什麼,早睡早起好身體,iphone爆炸了,天高破產了哈哈哈,新美互動組,測試數據啦,下班晚又不跑步了,天天這樣是會死人的,

真的嗎,我有點不相信,今天踩到狗屎了,扎心了,老鐵,我家養了一條狗,你今天跑步了嗎,聽說下雨天和巧克力更配哦],

});


推薦閱讀:

怎樣高效豐富bilibili彈幕/用戶屏蔽列表?
為什麼現在嗶哩嗶哩彈幕的素質越來越低?

TAG:彈幕 | 插件 |