基于CSS3 Transform的自适应响应式React封装组件以及对于Swiper.js的封装

这三个小的组件是笔者对常见的一些操作的React封装,性能不一定优化,只是为了方便使用,随手写的,不过如果有什么功能建议欢迎开Issue
Github系列Repos

ScalableComponent

借鉴了pageResponse这个移动端响应式插件

核心思想就是基于CSS3 Transform中的scale变换,根据屏幕尺寸与视觉设计稿的尺寸对比进行自动释放,以求达到较好的浏览效果,适合于懒人快速适配。

Usage

设置视口

源代码

/**
 * Created by apple on 16/6/30.
 */
import React, {Component, PropTypes} from "react";

export default class ScalableComponent extends Component {

    //设置属性值
    static propTypes = {
        mode: PropTypes.oneOf(['auto', 'contain', 'cover']), //选择的模式
        width: PropTypes.number, //视觉稿宽度
        height: PropTypes.number, //视觉稿高度
        origin: PropTypes.string,//缩放中心点
        wrapperBackgroundColor: PropTypes.string//背景颜色
    };

    /**
     * @function 构造函数
     * @param props
     */
    constructor(props) {

        super(props);

        let userAgent = navigator.userAgent;

        //判断是否为指定设备
        this.wp = userAgent.match(/Windows Phone ([\d.]+)/); //判断是否为WindowsPhone
        this.android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/); //判断是否为Android

        //获取设备的宽高比
        this.state = {
            deviceWidth: document.documentElement.clientWidth,
            deviceHeight: document.documentElement.clientHeight
        };


        //根据模式判断页面缩放比例
        this.mode = this.props.mode || "auto";

        //缩放中心
        this.origin = this.props.origin || "left top 0";

        //传入的视觉稿
        this.visualWidth = this.props.width || 320;

        this.visualHeight = this.props.height || 504;

        this.wrapperBackgroundColor = this.props.wrapperBackgroundColor || "black";

        this._calcScaleRatio = this._calcScaleRatio.bind(this);

        this._updateDimensions = this._updateDimensions.bind(this);

    }

    /**
     * @function 为了避免重绘,在ComponentWillMount之前
     */

    componentDidMount() {
        //监听页面尺寸变化
        window.addEventListener("resize", this._updateDimensions);
    }

    componentWillUnmount() {
        //移除页面尺寸变化监听
        window.removeEventListener("resize", this._updateDimensions);
    }

    /**
     * @function 更新屏幕尺寸
     * @private
     */
    _updateDimensions() {
        this.setState({
            deviceWidth: document.documentElement.clientWidth,
            deviceHeight: document.documentElement.clientHeight
        });
    }

    /**
     * @function 计算缩放参数
     * @private
     */
    _calcScaleRatio() {

        //默认缩放比例为1
        let scaleRatio = 1;

        let deviceAspectRatio = this.state.deviceWidth / this.state.deviceHeight;

        //计算传入的视觉稿的比
        let visualAspectRatio = this.visualWidth / this.visualHeight;

        //计算缩放比
        if (this.mode === "contain") {
            //如果是包含模式,根据宽高中较大值进行缩放
            scaleRatio = deviceAspectRatio > visualAspectRatio ? this.state.deviceHeight / this.visualHeight : this.state.deviceWidth / this.visualWidth;


        } else if (this.mode === "cover") {

            scaleRatio = deviceAspectRatio < visualAspectRatio ? this.state.deviceHeight / this.visualHeight : this.state.deviceWidth / this.visualWidth;

        } else {

            scaleRatio = this.state.deviceWidth / this.visualWidth;

        }

        return scaleRatio;

    }

    /**
     * @function 默认的渲染函数
     * @returns {XML}
     */
    render() {

        const scaleRatio = this._calcScaleRatio();

        //设置包裹层样式
        let wrapperStyle = {
            position: "relative",
            width: "100%",
            height: "100%",
            backgroundColor: this.wrapperBackgroundColor,
            overflow: "hidden"
        };

        //设置内部元素的缩放属性
        let scaleStyle = {
            width: this.visualWidth,
            height: this.visualHeight,
            WebkitTransformOrigin: this.origin,
            transformOrigin: this.origin,
            WebkitTransform: `scale(${scaleRatio})`,
            transform: `scale(${scaleRatio})`
        };

        // 兼容android 2.3.5系统下body高度不自动刷新的bug
        if (this.mode === "auto" && this.android) {
            document.body.style.height = this.visualHeight * scaleRatio + "px";

        } else if (this.mode === "contain" || this.mode === "cover") {
            //如果是contain模式
            //设置为绝对定位
            scaleStyle.position = "absolute";

            scaleStyle.left = (this.state.deviceWidth - this.visualWidth) / 2 + "px";

            scaleStyle.top = (this.state.deviceHeight - this.visualHeight) / 2 + "px";

            scaleStyle.WebkitTransformOrigin = "center center 0";

            scaleStyle.transformOrigin = "center center 0";

            //阻止默认滑屏事件
            if (this.wp) {
                document.body.style.msTouchAction = "none";
            } else {
                document.ontouchmove = function (e) {
                    e.preventDefault()
                }
            }
        }

        return (
{/*直接将子元素放置在这里*/} {this.props.children}
) } }

Mode

Contain

Contain模式即保证页面完全包含在浏览器窗口中,在保证页面的宽高比情况下调整页面的宽度或者高度中的较大者,使得页面水平垂直居中,左右或上下可能出现空白。

Cover

Cover模式即使页面完全覆盖浏览器窗口,保持页面的宽高比,调整页面的宽度或高度(较小者),页面水平垂直居中,超出浏览器窗口左右或上下的内容会被隐藏

Auto

保持页面的宽高比,调整页面的宽度,使页面宽度完全包含在浏览器窗口中

Demo:Swiper封装

笔者一开始只是想对于Swiper.js做简单的封装,但是后来发现好像Swiper.js没有官方的isomorphic版本,最终为了方便还是选择封装一个同时加载外部脚本与样式的小组件。

ReactExternalLoader:外部脚本/组件加载封装组件

Usage

import React from "react";
import {render} from "react-dom";
import {ReactExternalLoader} from "../external_loader";

render({alert("Loaded!");}}

>
    

HI

This is Demo For Scalable

, document.getElementById('root'));

源代码

/**
 * Created by apple on 16/7/1.
 */
import React, {Component, PropTypes} from "react";

//加载ES6 Promise Polyfill
require('es6-promise').polyfill();

//判断某个脚本是否已经被加载
const promises = {};

/**
 * @function 用于加载Script脚本
 * @param src
 * @returns {*}
 */
function loadScript(src) {

    //判断该脚本是否被加载过
    if (promises[src]) {
        return promises[src]
    }

    //构造一个Promise对象
    let promise = promises[src] = new Promise(resolve => {

        //创建一个script元素
        var el = document.createElement('script');

        var loaded = false;

        //设置加载完成的回调事件
        el.onload = el.onreadystatechange = () => {

            //防止重复加载
            if ((el.readyState && el.readyState !== 'complete' && el.readyState !== 'loaded') || loaded) {
                return false;
            }

            //加载完成后将该脚本的onload设置为null
            el.onload = el.onreadystatechange = null;

            loaded = true;

            resolve();
        };

        el.async = true;

        el.src = src;

        let head = document.getElementsByTagName('head')[0];

        head.insertBefore(el, head.firstChild);

    });

    return promise;

}

/**
 * @function 用于加载CSS文件
 * @param src
 * @returns {*}
 */
function loadCSS(src) {

    //判断该脚本是否被加载过
    if (promises[src]) {
        return promises[src]
    }

    //构造一个Promise对象
    let promise = promises[src] = new Promise(resolve => {

        //创建一个script元素
        var el = document.createElement('link');

        el.rel = "stylesheet";

        el.href = src;

        el.media = "only x";

        var loaded = false;

        //设置加载完成的回调事件
        el.onload = el.onreadystatechange = () => {

            //防止重复加载
            if ((el.readyState && el.readyState !== 'complete' && el.readyState !== 'loaded') || loaded) {
                return false;
            }

            //加载完成后将该脚本的onload设置为null
            el.onload = el.onreadystatechange = null;

            loaded = true;

            el.media = "all";

            resolve();
        };

        //获取文档头元素
        let head = document.getElementsByTagName('head')[0];

        //插入刚才创建好的元素
        head.insertBefore(el, head.firstChild);

    });

    return promise;

}

export class ReactExternalLoader extends Component {

    //设置默认的Props
    static defaultProps = {

        //需要加载的外部资源地址
        srcArray: ['javascript:void(0)'],

        //正在加载的指示
        loadingIndicator: (
Loading
), //加载完成回调 onLoad: () => { } }; //设置需要载入的Props static propTypes = { srcArray: React.PropTypes.array, loadingIndicator: React.PropTypes.object, onLoad: React.PropTypes.func }; //全局状态 state = { done: false }; /** * @function 组件加载完毕之前的回调 */ componentWillMount() { Promise.all( this.props.srcArray.map((src)=> { //判断是否为JS if (typeof src === "string" && src.indexOf(".js") > -1) { return loadScript(src); } else { return loadCSS(src); } }) ).then( ()=> { //设置加载完成 this.setState({ done: true }); //调用外部的完成回调 this.props.onLoad(); } ); } /** * @function 渲染方法 * @returns {*} */ render() { //如果加载完成 if (this.state.done) { //返回实际的子组件 return this.props.children } else { //返回加载指示 return this.props.loadingIndicator; } } }

Swiper封装与使用

Usage

/**
 * Created by apple on 16/7/4.
 */
import React from "react";
import {render} from "react-dom";
import ScalableComponent from "../scalable";
import {SwiperContainer, SwiperSlide} from "../../scroll/slider/swiper";


render(
    

        

            
                

HI Slide1

This is Demo For Scalable

HI Slide2

This is Demo For Scalable

, document.getElementById('root'));

最终效果:

你可能感兴趣的:(swiper.js,react.js)