ReactScaleView
                                
                                
                                
                                    ReactScaleView copied to clipboard
                            
                            
                            
                        大屏数据可视化,响应式适配方案。利用响应式的适配方式,针对不同的分辨率的屏幕,一次开发,多屏兼容。large-screen-data-visualization。
ReactScaleView
Introduction
ScaleView 组件库基于 React 封装,主要用于构建大屏(全屏)数据展示页面即数据可视化
利用响应式的适配方式,不管是在 PC 端,还是投放到大屏上,不管是 1440*768,1080p,还是 2k,4k 甚至更大分辨率的屏幕,都只需要 1 次适配,多屏幕兼容。
以 UI 设计图为基准,适配好一个尺寸,理论上可以支持任意相似屏幕比例的屏幕

响应式布局

在 1920px*1080px 的画布上,即使缩小到 1440*768,或者放大到 3840*2160,所有内容将自动缩放成相应比例值,也无需重新适配
Install
npm: npm i react-scale-view
或
yarn: yarn add react-scale-view
Demo
- yarn install
 - yarn start
 
storybook
- yarn install
 - yarn storybook
 
Use
1. 创建容器
根据 UI 的设计图,创建相应的画布大小,画布大小一般为 1920px*1080px
在父组件中引入 ScaleViewContainer,将对容器内的所有子组件进行缩放处理
例 1
import { ScaleViewContainer } from 'react-scale-view';
const Component = () => {
  return (
    <ScaleViewContainer
      config={{
        width: 1920, // (必选)容器宽度;如 1920,
        height: 1080, // (必选)容器高度;如 1080,
        scaleType: 'FULL_SCREEN',
      }}
    ></ScaleViewContainer>
  );
};
例 2
ScaleViewContainer 与 ScaleViewItem 不一定是父子关系,也可以是爷孙关系。
因此可以将 ScaleViewContainer 提取到最外层中,对全局组件进行适配
// 1. 新建配置文件, 如./config.js
export default {
  container: {
    width: 1920, // (必选)容器宽度;如 1920,
    height: 1080, // (必选)容器高度;如 1080,
    scaleType: 'FULL_SCREEN',
  },
};
// 2. 实例化容器组件, 如./DemoDataV.js
import { ScaleViewContainer } from 'react-scale-view';
import Config from './config';
const DemoDataV = props => {
  return (
    <ScaleViewContainer
      config={Config.container}
      className={styles.scaleViewContainer}
    >
      <Switch>
        <Route
          path="/demo"
          exact
          render={() => <Redirect to="/demo/data-v1" />}
        />
        <Route path="/demo/data-v1" component={LoadableDataV1} />
        <Route path="/demo/data-v2" component={LoadableDataV2} />
        <Route path="/demo/data-v3" component={LoadableDataV3} />
      </Switch>
    </ScaleViewContainer>
  );
};
export default DemoDataV;
2. 创建子组件
例 1
import { ScaleViewContainer, ScaleViewItem } from 'react-scale-view';
const Parent = () => {
  return (
    <ScaleViewContainer
      config={{
        width: 1920, // (必选)容器宽度;如 1920,
        height: 1080, // (必选)容器高度;如 1080,
        scaleType: 'FULL_SCREEN',
      }}
    >
      <Children></Children>
    </ScaleViewContainer>
  );
};
const Children = () => {
  return (
    <ScaleViewItem
      config={{
        id: 'headerChart',
        style: { left: 0, top: 0, width: '100%', height: 200 },
        transition: {
          anim: 'slide',
          from: 'top',
          timeout: 300,
          delay: 100,
        },
        contentStyle: { background: 'rgba(0,0,0,0.5)' },
      }}
    ></ScaleViewItem>
  );
};
例 2
// 1. 为了代码简介,可以吧配置信息统一放在配置文件中
// ./config.js
export default {
  topChart: {
    id: 'topChart',
    style: {
      left: 0,
      top: 0,
      right: 0,
      width: '100%',
      height: 200,
    },
    transition: {
      anim: 'slide',
      from: 'top',
      timeout: 300,
      delay: 300,
    },
    mode: 'adaptWidth',
  },
  leftChart: {
    id: 'leftChart',
    style: {
      left: 0,
      bottom: 0,
      width: 400,
    },
    transition: {
      anim: 'slide',
      from: 'left',
      timeout: 300,
      delay: 300,
    },
    mode: 'scaleXFix',
    relations: {
      layoutBelow: 'topChart',
    },
  },
  rightChart: {
    id: 'rightChart',
    style: {
      right: 0,
      bottom: 0,
      width: 460,
    },
    transition: {
      anim: 'slide',
      from: 'right',
      timeout: 300,
      delay: 300,
    },
    mode: 'scaleXFix',
    relations: {
      layoutBelow: 'topChart',
    },
  },
  mapChart: {
    style: {
      left: 0,
      top: 0,
      width: '100%',
      height: '100%',
    },
    mode: 'fixed',
  },
};
// 2. 实例化组件,在页面中引入ScaleViewItem
import React, { useEffect } from 'react';
import { ScaleViewItem } from 'react-scale-view';
import { Map } from '@/components';
import Config from './config';
import styles from './DataV1.module.less';
const DataV1 = props => {
  return (
    <>
      <ScaleViewItem config={Config.mapChart}>
        <Map></Map>
      </ScaleViewItem>
      <ScaleViewItem config={Config.leftChart} contentClass={styles.leftChart}>
        LeftChart
      </ScaleViewItem>
      <ScaleViewItem config={Config.topChart} contentClass={styles.topChart}>
        Top Chart
      </ScaleViewItem>
      <ScaleViewItem
        config={Config.rightChart}
        contentClass={styles.rightChart}
      >
        Right Chart
      </ScaleViewItem>
    </>
  );
};
export default DataV1;
Document
文档可参考 storybook,运行本项目命令 yarn storybook 即可看到相关文档
1. ScaleViewContainer
使用 ScaleViewContainer 作为大屏的容器组件,可以根据当前浏览器的像素宽高,将容器内的组件统一按照某个计算比值进行缩放。用户只需要按照设计稿还原 UI,使用该组件,可以在任何分辨率的浏览器中按比例尽可能还原。
| 参数 | 说明 | 类型 | 默认值 | 必选 | 
|---|---|---|---|---|
| config | 设置容器画布(内容区域)的尺寸及缩放模式 | object | 无 | 是 | 
| style | 容器的样式 | object | 无 | 否 | 
| className | 容器的 class | class | 无 | 否 | 
| contentStyle | 容器画布(内容区域)的样式 | object | 无 | 否 | 
| contentClass | 容器画布(内容区域)的 class | class | 无 | 否 | 
1.1 config
| 参数 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| width | 容器宽度 | Number | |
| height | 容器高度 | Number | |
| scaleType | 容器缩放模式:FULL_SCREEN适应全屏,ADAPT_HEIGHT宽度铺满,高度按比例缩放, ADAPT_WIDTH高度铺满,宽度按比例缩放 | 
String | 
2. ScaleViewItem
| 参数 | 说明 | 类型 | 默认值 | 必选 | 
|---|---|---|---|---|
| style | Item 容器的样式 | object | 无 | 否 | 
| mode | Item 容器的缩放模式:standard,fixed,scaleXFix,scaleYFix,adaptWidth,adaptHeight | 
string | standard | 否 | 
| transition | 进场动画 | object | 无 | 否 | 
| relations | Item 间相对关系 | object | 无 | 否 | 
| className | Item 容器的 class | class | 无 | 否 | 
| contentStyle | Item 内容区域的样式 | object | 无 | 否 | 
| contentClass | Item 内容区域的 class | class | 无 | 否 | 
| config | Item 统一设置(主要目的是减少组件参数数量):stylemodetransitionrelationscontentStyle | 
object | 无 | 否 | 
2.1 mode 缩放模式
| 参数 | 说明 | 类型 | 默认值 | 必选 | 
|---|---|---|---|---|
| mode='standard' | 跟随容器缩放,不改变 item 的 width、height、scale(默认) | string | 无 | 否 | 
| mode='fixed' | 跟随容器缩放,自动改变 item 的 width、height、scale。内容不会变形,但会被裁剪;使 item 大小不依赖 scale,而是通过改变宽高来适配;建议仅在地图上使用; | 
string | 无 | 否 | 
| mode='scaleXFix' | 跟随容器缩放,不改变 item 的 width、height,不改变 scaleY,自动改变 scaleX。内容不会变形。保持 scaleY,通过调整 scaleX 来保持内容不变形。 | string | 无 | 否 | 
| mode='scaleYFix' | 跟随容器缩放,不改变 item 的 width、height,不改变 scaleX,自动改变 scaleY。内容不会变形。保持 scaleX,通过调整 scaleY 来保持内容不变形。 | string | 无 | 否 | 
| mode='adaptWidth' | 跟隨容器縮放,宽度相对固定,高度自动缩放。 当 container.scaleX / container.scaleY > 1 时,自动改变 width,scaleX;当 container.scaleX / container.scaleY < 1 时,自动改变 scaleY,内容不会变形; | string | 无 | 否 | 
| mode='adaptHeight' | 跟随容器缩放,高度相对固定,宽度自动缩放。当 container.scaleX / container.scaleY > 1 时,自动改变 scaleX;当 container.scaleX / container.scaleY < 1 时,自动改变 height, scaleY,内容不会变形; | string | 无 | 否 | 
注意:由于大多数地图 api 都对地图上的鼠标事件进行了监听处理,对组件进行 transform 变化时会出现偏移,使用 mode='fixed'可以解决这一问题
2.2 transition 进场动画
| 参数 | 说明 | 类型 | 默认值 | 必选 | 
|---|---|---|---|---|
| anim | 进场动画方式:slideopacity | 
string | 无 | 是 | 
| from | 进场方向,仅当 anim='slide'时生效:lefttoprightbottom | 
string | 无 | 否 | 
| timeout | 执行时间 | number | 无 | 是 | 
| delay | 延迟时间 | number | 无 | 否 | 
// 例如
const transition = {
  anim: 'slide',
  from: 'left',
  timeout: 150,
  delay: 150,
};
2.3 relations Item 间相对关系
| 参数 | 说明 | 类型 | 默认值 | 必选 | 
|---|---|---|---|---|
| layoutBelow: id | 位于 id 元素正下方 | string | 无 | 否 | 
| layoutAbove: id | 位于 id 元素正上方 | string | 无 | 否 | 
| toLeftOf: id | 位于 id 元素左边 | string | 无 | 否 | 
| toRightOf: id | 位于 id 元素右边 | string | 无 | 否 | 
| alignLeft: id | 左边界与 id 元素的左边界对齐 | string | 无 | 否 | 
| alignRight: id | 右边界 id 元素的右边界对齐 | string | 无 | 否 | 
| alignTop: id | 上边界与 id 上边界对齐 | string | 无 | 否 | 
| alignBottom: id | 下边界与 id 下边界对齐 | string | 无 | 否 | 
// 例如
const relations = {
  layoutBelow: 'chart1',
  layoutAbove: 'chart2',
};
3. ScaleViewContext
当组件不在 ScaleViewContainer 的子组件中,而在孙子组件中,由于使用 ScaleViewItem 层级嵌套太多容易出现样式错乱问题,这时可以使用 ScaleViewContext 来获取 ScaleViewContainer 的容器参数,来对组件做适配。 注意:ScaleViewContext 与 ScaleViewContainer 必须在同一上下文
import React from 'react';
import { ScaleViewContext } from 'react-scale-view';
const ContextDemo = props => {
  return (
    <ScaleViewContext.Consumer>
      {({ size }) => {
        return (
          <div style={{ fontSize: 40 }}>
            <div>size.scaleX: {size.scaleX}</div>
            <div>size.scaleY: {size.scaleY}</div>
            <div>size.width: {size.width}</div>
            <div>size.height: {size.height}</div>
          </div>
        );
      }}
    </ScaleViewContext.Consumer>
  );
};
export default ContextDemo;
4. useSize
useSize 使用 hooks 方式获取容器参数
import React from 'react';
import { useSize } from 'react-scale-view';
const Demo = props => {
  const size = useSize();
  return (
    <div style={{ fontSize: 40 }}>
      <div>size.scaleX: {size.scaleX}</div>
      <div>size.scaleY: {size.scaleY}</div>
      <div>size.width: {size.width}</div>
      <div>size.height: {size.height}</div>
    </div>
  );
};
export default ContextDemo;
5. withSize
withSize 使用 HOC 方式注入容器参数
import React from 'react';
import { withSize } from 'react-scale-view';
const Demo = props => {
  const { size } = props;
  return (
    <div style={{ fontSize: 40 }}>
      <div>size.scaleX: {size.scaleX}</div>
      <div>size.scaleY: {size.scaleY}</div>
      <div>size.width: {size.width}</div>
      <div>size.height: {size.height}</div>
    </div>
  );
};
export default withSize(ContextDemo);
完整例子
import React, { Component } from 'react';
import { ScaleViewContainer, ScaleViewItem } from 'react-scale-view';
import './App.css';
class App extends Component {
  render() {
    return (
      <ScaleViewContainer
        config={{
          width: 1920, // (必选)容器宽度;如 1920,
          height: 1080, // (必选)容器高度;如 1080,
          scaleType: 'FULL_SCREEN',
        }}
      >
        <ScaleViewItem
          config={{
            id: 'headerChart',
            style: { left: 0, top: 0, width: '100%', height: 200 },
            transition: {
              anim: 'slide',
              from: 'top',
              timeout: 300,
              delay: 100,
            },
            contentStyle: { background: 'rgba(0,0,0,0.5)' },
          }}
        ></ScaleViewItem>
        <ScaleViewItem
          config={{
            style: { left: 0, top: 0, bottom: 0, width: 450 },
            transition: {
              anim: 'slide',
              from: 'left',
              timeout: 300,
              delay: 100,
            },
            contentStyle: { background: 'rgba(0,0,0,0.5)' },
            relations: {
              layoutBelow: 'headerChart',
            },
          }}
        ></ScaleViewItem>
        <ScaleViewItem
          config={{
            style: { right: 0, top: 0, bottom: 0, width: 450 },
            transition: {
              anim: 'slide',
              from: 'right',
              timeout: 300,
              delay: 100,
            },
            contentStyle: { background: 'rgba(0,0,0,0.5)' },
            relations: {
              layoutBelow: 'headerChart',
            },
          }}
        ></ScaleViewItem>
      </ScaleViewContainer>
    );
  }
}
export default App;
运行效果

注意:在开发中使用一些第三方的 UI 库或 API 时,往往会脱离上下文(Context),或在 ScaleViewContainer 外渲染组件
如:
1. ant-design 会选在 body 下渲染 modal,popcontainer 等
2. 如在地图上创建图标(Marker),海量图(MassMark),弹窗(InfoWindow)等
导致这些组件脱离了 ScaleViewContainer 适配范围。针对这些情况,可以使用ScaleViewContext,useSize,withSize获取 ScaleViewContainer 的缩放比例及容器大小,对第三方组件进行 transform 变化。