Commit fbbaa163 authored by 熊东起's avatar 熊东起

first

parents
File added
# Created by .ignore support plugin (hsz.mobi)
.temp
node_modules
dist
/src-js
yarn.lock
package-template.iml
/test/src/bundle.js
/test/src/meta.json
types
# Created by .ignore support plugin (hsz.mobi)
/src/
/test/
*.iml
.temp
/assets
/scripts
tsconfig.json
readme-tpl.md
This diff is collapsed.
{
"name": "@spark/Barrage",
"version": "1.0.0",
"main": "dist/bundle.js",
"types": "types/index.d.ts",
"license": "MIT",
"scripts": {
"dev": "npm-run-all -p -r dev:**",
"dev:me": "spark package pack -s src -o test/src/bundle.js -w",
"dev:test": "cd test && npm run dev",
"build": "tsc --outDir 'src-js' -t es2017 -d --declarationDir 'types' --jsx preserve && node scripts/copy-others.js 'src-js'",
"pack": "spark package pack -s src -o dist/bundle.js -p",
"declare": "tsc -d --declarationDir 'types' --emitDeclarationOnly",
"pub": "npm run build && read -p 'Input version: ' version && read -p 'Input remark: ' remark && spark package publish -s src -v $version -r $remark"
},
"dependencies": {},
"devDependencies": {
"tslib": "^2.0.1",
"fs-extra": "^9.0.1",
"less": "^4.1.0",
"npm-run-all": "^4.1.5",
"typescript": "^4.1.3",
"@types/react": "^16.9.56"
}
}
# ${id}
${name}
${desc}
## Install
`yarn add @spark/${id}`
## Usage
```js
import {${exportIds}} from '@spark/${id}'
```
${exports}
## Contribute
1. `yarn dev` to develop package
2. `cd test && yarn && yarn dev` to develop test
# Barrage
## Install
`yarn add @spark/Barrage`
## Usage
```js
import {...} from '@spark/Barrage'
```
## Contribute
1. `yarn dev` to develop package
2. `cd test && yarn && yarn dev` to develop test
## Publish
`npm run pub`
/**
* Created by rockyl on 2021/1/18.
*/
const fs = require('fs-extra')
const path = require('path')
function filter(file) {
let extname = path.extname(file);
return !(extname === '.tsx' || extname === '.ts');
}
fs.copySync('src', process.argv[2], {filter})
/**
* Created by _xdq on 2021/03/24.
*/
import Track from "./track";
import { MangerConfig } from "./type";
import { BarrageObj } from './type';
interface TrackIterationHandler<T extends BarrageObj> {
(track: Track<T>, index: number, trackArr: Track<T>[]): void
}
abstract class BaseMgr<T extends BarrageObj> {
protected trackWidth: number;
protected trackHeight: number;
protected duration: number;
protected maxTrackCount: number;
protected tracks: Array<Track<T>> = [];
/** 弹幕等待队列 */
waitBarrageList: T[] = [];
constructor(options: MangerConfig) {
this.trackWidth = options.trackWidth;
this.trackHeight = options.trackHeight;
this.duration = options.duration;
this.maxTrackCount = options.maxTrackCount;
/** 生成管道 */
console.log("99",options)
for (let i = 0; i < options.maxTrackCount; i++) {
this.tracks[i] = new Track();
}
}
/** 管道迭代器 */
trackIteration(iterationhandler: TrackIterationHandler<T>) {
for(let i = 0;i < this.tracks.length;++i){
iterationhandler(this.tracks[i], i, this.tracks);
}
}
/** 重置管道宽高 */
reset(width?: number, height?: number) {
width && (this.trackWidth = width);
height && (this.trackHeight = height);
}
/** 添加到等待队列 */
pushWaitList(barrageObj: T){
this.waitBarrageList.push(barrageObj);
}
/** 添加弹幕到管道 */
abstract addBarrage(barrageItem: T): boolean
/** 从等待队列中选取弹幕 */
abstract extractBarrage(): void
/** 移动 */
abstract animate(): void
}
export default BaseMgr;
\ No newline at end of file
import { BarrageObj } from './type';
/**
* Created by _xdq on 2021/03/24.
*/
import BaseMgr from "./base";
import MoveManger from "./moveManger";
import { BarrageConfig } from "./type";
import { deepMerge } from "./utlis";
type BarrageCfg = Partial<BarrageConfig>;
const defaultCfg: BarrageConfig = {
maxTrackCount: 3,
duration: 3000
}
class Barrage {
/** 节点 */
_el: HTMLElement;
/** 初始配置 */
_config: BarrageCfg;
/** 移动管理 */
_barrageMgr: BaseMgr<BarrageObj>;
/** 动画 */
_animation: number | null = null;
constructor(el: HTMLElement, config?: BarrageCfg) {
this._config = deepMerge(defaultCfg, config || {});
this._el = el;
console.log("el----->>>>>", this._el.offsetHeight);
const _options = {
trackWidth: this._el.offsetWidth,
trackHeight: Math.floor(this._el.offsetHeight / this._config.maxTrackCount),
duration: this._config.duration,
maxTrackCount: this._config.maxTrackCount
}
this._barrageMgr = new MoveManger(_options);
const _children = this._el.children;
for (let i in _children) {
if (_children[i] instanceof HTMLElement) {
const node: HTMLElement = _children[i] as HTMLElement;
let _curBarrage: BarrageObj = {
el: node,
offset: _options.trackWidth,
speed: 2,
width: node.offsetWidth
};
console.log("999---->>>",_curBarrage)
this._barrageMgr.pushWaitList(_curBarrage);
}
}
}
/** 弹幕开始 */
start(){
if(this._animation) return;
this._animation = requestAnimationFrame(this.animate.bind(this));
}
/** 帧动画 */
animate(){
if(!this._barrageMgr) return;
this._barrageMgr.animate();
this._animation = requestAnimationFrame(this.animate.bind(this));
}
}
export default Barrage;
\ No newline at end of file
/**
* Created by _xdq on 2021/03/24.
*/
import BaseMgr from "./base";
import Track from "./track";
import { BarrageObj, MangerConfig } from "./type";
import { getEndItem, isEmptyArray } from "./utlis";
class MoveManger<T extends BarrageObj> extends BaseMgr<T> {
constructor(options: MangerConfig) {
super(options);
}
private get _defaultSpeed(): number {
return (this.trackWidth / this.duration) * 16.6;
}
private get _randomSpeed(): number {
return 0.8 + Math.random() * 1.3;
}
addBarrage(barrageItem: T): boolean {
const trackId = this.findOneTrack();
if (trackId === -1) return false;
console.log("77777", trackId)
const _track = this.tracks[trackId];
const _trackOffset = _track.offset;
const _trackWidth = this.trackWidth;
const _barrageWidth = barrageItem.width;
//计算速度
let speed: number = this._defaultSpeed;
// if(isEmptyArray(_track._barrages)){
// speed = this._defaultSpeed;
// }else{
// const w = getEndItem<T>(_track._barrages);
// console.log("000---000",w)
// speed = this._defaultSpeed;
// }
// barrageItem.el.style.transform = `translateX(750px)`;
barrageItem.speed = speed;
barrageItem.offset = _trackWidth;
_track.push(barrageItem);
_track.offset = _trackWidth + _barrageWidth * Math.floor(Math.random() * 20);
return true;
}
/** 选择一条弹幕 */
extractBarrage(): void {
let _curBarrage: boolean = false;
for (let i = 0; i < this.waitBarrageList.length;) {
_curBarrage = this.addBarrage(this.waitBarrageList[i]);
if (!_curBarrage) break;
this.waitBarrageList.shift();
}
}
/** 每一帧动画 */
animate(): void {
let _this = this;
this.extractBarrage();
let _recoverIndex = -1,
_recoverBarrage: T = null;
const _trackH = this.trackHeight;
const _trackW = this.trackWidth;
this.trackIteration((trackItem: Track<T>, index: number) => {
trackItem.barrageIteration((barrageItem: T, _index: number) => {
barrageItem.el.style.transform = `translate(${barrageItem.offset}px,${index * _trackH}px)`;
barrageItem.offset -= barrageItem.speed;
//回收
if (barrageItem.offset < 0 && Math.abs(barrageItem.offset) > _trackW) {
console.log("回收++++")
trackItem.removeBarrage(_index);
this.recoverBarrage(barrageItem);
}
});
trackItem.update();
});
}
/** 寻找管道 */
findOneTrack(): number {
let _id = -1;
let _max = -Infinity;
this.trackIteration((trackItem: Track<T>, index: number) => {
const _offset = trackItem.offset;
// if (_offset >= this.trackWidth) return;
// const _interval = this.trackWidth - _offset;
// if (_interval > _max) {
// _id = index;
// _max = _interval;
// }
const _interval = Math.abs(_offset) - this.trackWidth;
if(Math.abs(_offset) - this.trackWidth > 0) return;
//TODO
// if(_interval > )
});
return _id;
}
/** 回收弹幕 */
recoverBarrage(bartrage: T) {
// bartrage.offset = this.trackWidth;
bartrage.speed = this._defaultSpeed;
this.pushWaitList(bartrage);
}
}
export default MoveManger;
/**
* Created by _xdq on 2021/03/24.
*/
import { BarrageObj } from './type';
/** 迭代器 */
interface BarrageIterationHandler<T extends BarrageObj> {
(barrage: T, index: number, barrageList: T[]): void
}
/** 管道类 */
class Track<T extends BarrageObj> {
_barrages: T[] = [];
private _offset: number = 0;
/** 管道已经占据空间 */
get offset(): number {
return this._offset;
}
set offset(v: number) {
if (v == this.offset) return;
this._offset = v;
}
/** 迭代弹幕 */
barrageIteration(iterationHandler: BarrageIterationHandler<T>) {
this._barrages.forEach((barrageItem, index) => {
iterationHandler(barrageItem, index, this._barrages);
});
}
/** 重置 */
reset() {
this._barrages = [];
this._offset = 0;
}
/** 添加所有的弹幕 */
push(barrageItem: T) {
this._barrages.push(barrageItem);
}
/** 移除弹幕 */
removeBarrage(index: number) {
if (index < 0 || index >= this._barrages.length) return;
this._barrages.splice(index, 1);
}
/** 更新 */
update() {
const _endBarrage = this._barrages[this._barrages.length - 1];
if (_endBarrage) {
const { offset,width } = _endBarrage;
this.offset = offset-width;
}
}
}
export default Track;
\ No newline at end of file
/** 弹幕配置接口 */
export interface BarrageConfig {
/** 最大管道数量 */
maxTrackCount: number;
/** 弹幕持续时间 */
duration: number;
}
/** 弹幕对象接口 */
export interface BarrageObj {
/** 节点 */
el: HTMLElement;
/** 偏移 */
offset: number;
/** 速度 */
speed: number;
/** 弹幕的宽度 */
width: number;
}
/** 管理类配置接口 */
export interface MangerConfig {
/** 管道宽 */
trackWidth: number;
/** 管道高 */
trackHeight: number;
/** 弹幕持续时间 */
duration: number;
/** 最大管道数量 */
maxTrackCount: number;
}
\ No newline at end of file
/**
* Created by _xdq on 2021/03/24.
*/
export function isEmptyArray<T>(array: T[]): boolean {
return array.length === 0
}
export const isNull = function (o: any): o is null {
return o === null;
}
export const isUndefined = function (o: any): o is undefined {
return typeof o === 'undefined';
}
export const isObject = function (o: any): o is object {
return typeof o === 'object' && o !== null;
}
export const isPlainObject = function (o: any): o is object {
return Object.prototype.toString.call(o) === '[object Object]';
}
/** 深拷贝 */
export function deepMerge(...objects: any[]): any {
const ret: any = {};
objects.forEach(obj => {
if (isNull(obj) || isUndefined(obj)) {
return;
}
Object.keys(obj).forEach((key: string) => {
if (!ret.hasOwnProperty(key)) {
ret[key] = obj[key];
} else {
if (Array.isArray(obj[key])) {
ret[key] = obj[key];
} else if (isPlainObject(obj[key])) {
ret[key] = deepMerge(ret[key], obj[key]);
} else {
ret[key] = obj[key];
}
}
});
});
return ret;
}
/** 获取最后一个数组元素 */
export function getEndItem<T>(arr: T[]): T {
return arr[arr.length - 1];
}
/** 帧动画 */
export const requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame
export const cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame
\ No newline at end of file
declare function getMetaConfig(id: string);
declare const PROCESS = 1;
declare const DOM_COMPONENT = 2;
declare const CANVAS_WIDGET = 3;
/**
* Created by _xdq on 2021/03/24.
*/
export {default as Barrage} from "./barrage";
\ No newline at end of file
{
"id": "Barrage",
"name": "测试包",
"desc": "实现了测试包"
}
EXTEND_ESLINT = true
/**
* Created by rockyl on 2020/11/20.
*/
const path = require('path');
module.exports = {
devServer: function(configFunction){
return function(proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
config.contentBase = [path.resolve('public'), path.resolve('../')];
return config;
};
}
}
{
"name": "test",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.3",
"spark-utils": "^0.0.9",
"spark-test-lib": "^1.0.1"
},
"scripts": {
"dev": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-scripts ",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"sass": "^1.29.0",
"react-app-rewired": "^2.1.6"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>test: Barrage</title>
<script src="//yun.duiba.com.cn/js-libs/rem/1.1.0/rem.min.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
import React, {useState,useRef,useEffect} from 'react';
import './App.scss';
import {Barrage} from "./bundle";
import {TestBed, InputNumber} from 'spark-test-lib'
function App() {
return (
<TestItem/>
);
}
function TestItem(){
const renderData = () => {
return (
<>
<div>1</div>
<div>2</div>
<div>3</div>
</>
)
}
const barrageContainer = useRef();
useEffect(() => {
let _barrage = new Barrage(barrageContainer.current,{});
setTimeout(() => {
_barrage.start();
},1000)
}, [])
return(
<div>
<div className="barrage-container" ref={barrageContainer}>
<div className="item"><span>11</span></div>
<div className="item">2</div>
<div className="item">3</div>
<div className="item">4</div>
<div className="item">5</div>
</div>
</div>
)
}
export default App;
.App {
label{
display: flex;
justify-content: space-between;
margin-bottom: 5px;
}
.text-wrap{
white-space:pre-wrap;
word-wrap:break-word;
}
.barrage-container{
width: 750px;
height: 500px;
position: relative;
.item{
width: 100px;
height: 60px;
background-color: #cfcfcfcf;
position: absolute;
top: 0;
right: 0;
}
}
}
.barrage-container{
width: 100vw;
height: 500px;
position: relative;
.item{
width: 100px;
height: 20px;
text-align: center;
background-color: #000000;
color: #fff;
position: absolute;
top: 0;
right: 0;
border-radius: 20px;
}
}
\ No newline at end of file
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
html, body, #root{
width: 100%;
height: 100%;
}
body {
margin: 0;
font-size: 14px;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
{
"compilerOptions": {
"module": "ES6",
"target": "ES5",
"jsx": "react",
"allowJs": true,
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"lib": [
"ES2015",
"DOM"
]
},
"include": ["src"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment