父画面给模态框加css,Recat 自定义模态框 随心所欲CSS动画

师兄最近深陷React的坑不能自拔~觉得React似乎比angular或者ionic要更有意思(不愧脸书),在业务代码上跑多了,就不断有想法:项目中使用了ant-mobile 但是这个UI框架给人的感觉总是怪怪的(感觉是后娘样的,只能凑合两字形容),于是师兄索性针对于气死人不偿命的模态框做出一个 react版的百变模态框,还可以扩展哦。(目前还没有上网查,应该已经有人做出来了,不管了,正文要开始了~(期待))

一、先上效果图

ActionSheet

5d6516dbd2a4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

demo1.gif

modal

5d6516dbd2a4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

demo2.gif

以上两图是师兄目前做出的两个demo

是不是动感十足,丝毫不逊色于阿里的ant,而且还要比他更强大。

why?

原因有三:

一、彻头彻尾的DIY

例如demo1的actionSheet 里面的内容都是用react的JSX实现,也就是说除了外面的框框(白板)里面全部可以自定义,感觉就像自己写的一样6。(后面会展示代码)

二、动画可以自定义

css动画样式随便修改,想用什么都可以,(淡入淡出、上来下去...)还有师兄自己研究的坐标飞入动画(哪来哪去)

三、扩展性强,可以直接DIY自己的组件

觉得demo太少?自己动手丰衣足食,该组件支持扩展直接自定义自己的组件模块,然后直接插入即可使用

二、思路

讲了那么多,都是外在的效果核心还是思路。

外部一个顶级父类或者总出入口,主要负责模块的调度和动画的执行,而下级则是自定义的model,比如上面的actionsheet和modal,下级主要负责样式和model的位置同时定义属于自己的动画,这样解耦扩展性很强。

三、核心代码

一、使用

这里有必要讲一下,这个demo中的actionsheet可以做到动态刷新,也就是例如上面那个demo1中actionsheet中的题目按钮,当点击某一个,即能便为选中状态。

5d6516dbd2a4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

demo3.gif

/**

* 更新actionSheet

* @param {*} screenX

* @param {*} screenY

*/

updateXAST(screenX, screenY) {

console.log(this.props.selfData, "pppppp");

let content = (

{this.props.selfData.map((item, index) => (

onClick={(e) => { this.actionSheetBtnClick(item, index); }}>{(index + 1)}

))}

onClick={(e) => {

this.submitAnswer();

}}>提交答案

);

let skins = {

type: "XActionSheet",//"XModel",

title: "这是actionshee的标题",//标题

showTitle: false,

content: content,//内容jsx

top: "",//距离顶部 :20%

auto_height: false,//是否是自动扩展 true/false

clickElementClose: "",//点击元素 关闭model

x: screenX || this.state.skin.x,

y: screenY || this.state.skin.y,

//flyModel: true,

}

UtilService.showSuperModel(skins)

// this.setState({skin:skins},()=>{superModel.initModel(skins)});

//this.setState({ skin: skins }, () => { UtilService.showSuperModel(skins) });

//UtilService 中的代码

let superModel = new SuperModel();

static showSuperModel(skin) {

superModel.initModel(skin);

}

}

二、暂且叫超级model吧

它是一个总控中心负责模块选择和动画的执行

import React from 'react';

import ReactDOM from 'react-dom';

import './superModel.css';

import '../../app/animate.css'

/**

* 可调用模块

*/

import { XModel } from '../sonModel/XModel/XModel';//自定义模态框

import { XActionSheet } from '../sonModel/XActionSheet/XActionSheet'

import { UtilService } from '../UtilService/utilService';

//关键字

let _ROOT = "__AppModel_Root";

let _MODEL = "AppModel";

let _BODY = "statement-body-m";

let _BACK = "AppModel_background";

let superModelSkin = {

_ROOT: "",

_MODEL: "",

_BODY: "",

_BACK: "",

_ACTION: "",

};

let superModelOption;

let _HTML ;

export class SuperModel extends React.Component {

ID;

model;

content_m;

constructor(props) {

super(props);

this.showModel = this.showModel.bind(this);

this.getModel = this.getModel.bind(this);

this.closeModelByAnimate = this.closeModelByAnimate.bind(this);

this.ID=Math.random();

}

componentDidMount() {

if(!this.ID)this.ID=UtilService.getRandomStr();

if (!this.model) this.getModel();

this.showModel();

}

componentWillUnmount() {

this.clearSelf();

}

componentWillReceiveProps(nextProps,nextState) {}

componentWillUpdate( nextProps, nextState){}

createSuperModel() {

let temp = document.getElementById(superModelSkin._ROOT);

if (!temp) {

let html = document.createElement("div");

html.setAttribute("id", superModelSkin._ROOT);

document.getElementsByTagName("body")[0].appendChild(html);

}

}

//初始化

initModel(skin) {

if(!this.ID)this.ID=Math.random();

superModelOption = skin;

this.skinSwitch(skin);//选择皮肤

this.createSuperModel();

console.log(this.model,"model");

if (!this.model) {

console.log("====================ReactDOM.render======================");

ReactDOM.render(, document.getElementById(superModelSkin._ROOT));

}

}

paramHandler(skin) {

if (skin.top) {//距离顶部

if (this.content_m) {

this.content_m.style.marginTop = 0;

this.content_m.style.top = skin.top;

}

}

if (skin.closeClear) {

let closeClear = skin.closeClear;

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

if (closeClear[i].indexOf(".")) {

//待做

}

}

}

}

showModel() {

superModelOption = this.props.skin;

//参数处理

this.paramHandler(superModelOption);

//选择模块组件

//this.modelSwitch();

//通过动画展示actionSheet

this.model && this.showComponentByFade();

}

getModel() {

if (this.model) console.log("model alive");

if (!this.model) this.model = document.getElementById(superModelSkin._MODEL);

if (!this.content_m) this.content_m = document.getElementsByClassName(superModelSkin._BODY)[0];

}

/**

* 供外部方法直接关闭

* @param {*} option

*/

closeModelByOutUser(option) {

return new Promise((resolve, reject) => {

this.getModel();

this.closeModelByAnimate(option, () => {

resolve();

});

});

}

/**

* 内部关闭

*/

closeModelByAnimate(options, callback) {

let option = options ? options : {

x: superModelOption.x || this.props.skin.x,

y: superModelOption.y || this.props.skin.y,

flyModel: superModelOption.flyModel || this.props.skin.flyModel,

}

animateAction.hideModelWithCss(this.model, option, callback,

() => {

this.clearSelf();

});

}

clearSelf(){

this.ID=null;

this.model = null;

this.content_m=null;

superModelOption = null;

superModelSkin = null;

_HTML = null;

}

showComponentByFade() {

animateAction.initAnimateAction(superModelSkin._ACTION);

let option = {

x: superModelOption.x || this.props.skin.x,

y: superModelOption.y || this.props.skin.y,

flyModel: superModelOption.flyModel || this.props.skin.flyModel,

}

animateAction.showModelWithCss(this.model, option);

}

/**

* 皮肤选择器

* @param {*} type

*/

skinSwitch(skin) {

switch (skin.type) {

case "XModel":

superModelSkin = XModel.XModelSkin;

break;

case "XActionSheet":

superModelSkin = XActionSheet.XActionSheetSkin;

break;

}

//css 动画

if(skin.animate)superModelSkin._ACTION=skin.animate;

}

/**

* 模式选择器 看到这里就明白 扩展是怎么回事了

*/

modelSwitch() {

if (!this.model) this.getModel();

//if(_HTML)return;

let pa = superModelOption || this.props && this.props.skin;

if (this.props) {

switch (pa.type) {

case "XModel":

_HTML = (

closeModelByAnimate={this.closeModelByAnimate}

initDataCallBack={() => { }}>

);

break;

case "XActionSheet":

_HTML = (

closeModelByAnimate={this.closeModelByAnimate}

initDataCallBack={() => { }}>

);

break;

}

}

}

render() {

//console.log(this,"render");

if(!this.ID)this.ID=UtilService.getRandomStr().substring(0,10);

this.modelSwitch();

return (_HTML);

}

}

let Fadeflag = true;

export class AnimateAction {

_ACTION = {

_IN: [],

_OUT: [],

_OUT_TIME: 200,

};

initAnimateAction(action) {

this._ACTION = action;

}

/**

* 动画显示

* @param {*} obj

* @param {*} option

* @param {*} callback

*/

showModelWithCss(obj, option, callback) {

document.body.style.overflow = "hidden";

//显示背景

let AppBack = document.getElementsByClassName(superModelSkin._BACK)[0];

AppBack.classList.add("backgroundFadeIn");

//console.log(option);

//监听背景点击

obj.addEventListener("click", (event) => {

if (event.target === event.currentTarget) {

this.hideModelWithCss(obj, option, callback);

}

});

if (!option.flyModel) {//中心显示模式

this.animateActionFnc(obj, this._ACTION._IN);

this.animateEndFnc(obj, this._ACTION._IN);

obj.style.opacity = 1;

callback && callback();

} else {//飞行模式

this.show(obj, option, callback, () => {

this.animateActionFnc(obj, this._ACTION._IN);

this.animateEndFnc(obj, this._ACTION._IN);

});

}

}

/**

* 动画隐藏

* @param {*} obj

* @param {*} option

* @param {*} callback

* @param {*} modelCallBack

*/

hideModelWithCss(obj, option, callback, modelCallBack) {

//console.log(option);

//隐藏背景

let AppBack = document.getElementsByClassName(superModelSkin._BACK)[0];

AppBack.classList.add("backgroundFadeOut");

if (!option.flyModel) {//中心隐藏模式

this.animateActionFnc(obj, this._ACTION._OUT);

setTimeout(() => {//去除model-root

obj.style.background = "";

document.body.style.overflow = "auto";

ReactDOM.render("", document.getElementById(superModelSkin._ROOT));

callback && callback();

modelCallBack && modelCallBack();

}, this._ACTION._OUT_TIME);

} else {//飞行模式

this.animateActionFnc(obj, this._ACTION._IN);

//this.animateEndFnc(obj, this._ACTION._IN);

// obj.classList.add("fade-out-XY");

setTimeout(() => {

this.hide(obj, option, callback, modelCallBack);

}, 100);

}

}

/**

* 动画执行

* @param {*} obj

* @param {*} action

*/

animateActionFnc(obj, action) {

for (let v in action) {

obj.classList.add(action[v]);

}

}

/**

* 动画结束回调

* @param {*} obj

*/

animateEndFnc(obj, action) {

let animationEnd = (function (el) {

let animations = {

animation: 'animationend',

OAnimation: 'oAnimationEnd',

MozAnimation: 'mozAnimationEnd',

WebkitAnimation: 'webkitAnimationEnd',

};

for (let t in animations) {

if (el.style[t] !== undefined) {

return animations[t];

}

}

})(document.createElement('div'));

obj.addEventListener(animationEnd, function () {

for (let v in action) {

obj.classList.remove(action[v]);

}

})

}

/**

*

* @param {Object} obj

*/

show(obj, option, callback, modelCallBack) {

// console.log("fade show", option);

if (!obj) return;

let count = 10;//动作总次数

let time = 20;//每次动作时间

let num = 0;

let _X = parseFloat(option.x);//y轴屏幕距离

let _Y = parseFloat(option.y);//x轴屏幕距离

let xleft = (_X / window.screen.width) * 100;

let xtop = (_Y / window.screen.height) * 100;

let xv = xtop / count;//X轴缩放速率

let yv = xleft / count//Y轴缩放速率

let shrink = xtop && xleft ? 1 : 0;//是否定位落地

if (shrink) {

obj.style.top = xtop + "%";

obj.style.left = xleft + "%";

xtop = this.saveFloat(xtop);

xleft = this.saveFloat(xleft);

xv = this.saveFloat(xv);

yv = this.saveFloat(yv);

} else {

obj.style.margin = "auto";//以中心点收缩

}

obj.style.overflow = "hidden";

//obj.style.background = "";

obj.style.width = 0;

obj.style.height = 0;

obj.style.setProperty("z-index", 99999);

//obj.style.background = "rgba(0,0,0,0.3)";

if (Fadeflag) {

let st = setInterval(() => {

num++;

Fadeflag = false;

if (shrink) {

xtop = xtop <= 0 ? 0 : this.saveFloat(xtop - xv);

xleft = xleft <= 0 ? 0 : this.saveFloat(xleft - yv);

obj.style.top = xtop + "%";

obj.style.left = xleft + "%";

} else {

if (num >= 8) num = count;//缓速

}

obj.style.opacity = 0.2;// num / count;

obj.style.width = (num * count) + "%";

obj.style.height = (num * count) + "%";

if (num >= count) {

if (shrink) {

obj.style.top = "0px";

obj.style.left = "0px";

}

obj.style.width = "100%";

obj.style.height = "100%";

obj.style.opacity = 1;

//obj.style.background="rgba(0,0,0,0.3)";

modelCallBack && modelCallBack();

clearInterval(st);

Fadeflag = true;

callback && callback();

}

}, time);

}

}

/**

* 淡出效果

* @param {Object} obj

*/

hide(obj, option, callback, modelCallBack) {

document.body.style.overflow = "auto";

if (!obj) return;

let count = 10;//动作次数

let time = 15;//每次动作时间

let num = count;

let _X = option.x;//y轴屏幕距离

let _Y = option.y;//x轴屏幕距离

let xleft = (_X / window.screen.width) * 100;

let xtop = (_Y / window.screen.height) * 100;

// option.x = (option.x / window.screen.width) * 100;

// option.y = (option.y / window.screen.height) * 100;

// let xtop = option.y;//x轴屏幕距离

// let xleft = option.x;//y轴屏幕距离

let mtop = 0;//x轴动作增量

let mleft = 0;//y轴动作增量

let xv = xtop / count;//X轴缩放速率

let yv = xleft / count//Y轴缩放速率

let shrink = xtop && xleft ? 1 : 0;//是否定位落地

if (shrink) {

xtop = this.saveFloat(xtop);

xleft = this.saveFloat(xleft);

xv = this.saveFloat(xv);

yv = this.saveFloat(yv);

} else {

obj.style.margin = "auto";//以中心点收缩

}

if (Fadeflag) {

obj.style.background = "";

let st = setInterval(() => {

num--;

Fadeflag = false;

if (shrink) {

if (num <= count / 5) num = 0;//缓速

mtop = mtop > xtop ? xtop : this.saveFloat(mtop + xv);

mleft = mleft > xleft ? xleft : this.saveFloat(mleft + yv);

obj.style.top = mtop + "%";

obj.style.left = mleft + "%";

} else {

//if(num<=count/2)num=0;//缓速

}

obj.style.opacity = num / count;

obj.style.width = (num * count) + "%";

obj.style.height = (num * count) + "%";

if (num <= 0) {

if (shrink) {

obj.style.top = xtop + "%";

obj.style.left = xleft + "%";

}

obj.style.setProperty("z-index", -100);

obj.style.opacity = 0;

clearInterval(st);

Fadeflag = true;

ReactDOM.render("", document.getElementById(superModelSkin._ROOT));

callback && callback();

modelCallBack && modelCallBack();

}

}, time);

}

}

saveFloat(num, long) {

return parseFloat(num.toFixed(long ? long : 2));

}

}}

let animateAction = new AnimateAction();

export default SuperModel;

三、actionsheet

import React from 'react';

import ReactDOM from 'react-dom';

import closeBtn from '../../../img/closeBtn.png';

import './XActionSheet.css';

/**

* 底部ActionSheet

*/

const _ROOT = "AppXActionSheet_Root";

const _MODEL = "AppXActionSheet";

const _BODY = "actionSheet-body-m";

const _BACK = "AppModel_background";

export class XActionSheet extends React.Component {

static XActionSheetSkin={

_ROOT:_ROOT,

_MODEL:_MODEL,

_BODY:_BODY,

_BACK:_BACK,

_ACTION: {//动画

_IN:['slideInUp', 'animated','bounce', 'faster'],

_OUT:['slideInDown','animated', 'bounce', 'fast'],

_OUT_TIME:200,

} ,

}

constructor(props) {

super(props);

}

componentDidMount() {

}

componentWillUnmount() {

}

componentWillReceiveProps() {

}

render() {

//if(!this.props.skin.title)return(

return (

{/*

{ this.closeModelByAnimate() }}>
*/}

{this.props.skin.title}

%7BcloseBtn%7D { this.props.closeModelByAnimate(this.props.closeFnc) }} />

{/* dangerouslySetInnerHTML={{__html:content}} */}

{this.props.skin.content}

)

}

}

export default XActionSheet;

看到这是是不是觉得actionSheet这个demo很简单啊

四、结语

首先要多动手,然后才能发现问题,发现问题后,想办法解决,方法总比困难多。

路漫漫其修远兮,吾将上下而求索。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
_alignStart="@id/person_name" android:layout_below="@id/person_name" android:layout_marginTop="8dp" /> 好的,以下是一个简单的React组件,点击按钮后会出现一个模态框: ```jsx import React <TextView android:id="@+id/person_gender" android:layout_width="wrap_content" android:layout_height=", { useState } from 'react'; function Modal(props) { const [showModal, setShowModal] = useState(false); const handlewrap_content" android:textSize="14sp" android:layout_alignStart="@id/person_age" android:layout_below="@ButtonClick = () => { setShowModal(true); }; const handleCloseModal = () => { setShowModal(false); }; returnid/person_age" android:layout_marginTop="8dp" /> <TextView android:id="@+id/person_phone" ( <div> <button onClick={handleButtonClick}> {props.buttonText} </button> {showModal && ( android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android <div className="modal"> <div className="modal-content"> <span className="close" onClick={handleCloseModal}> :layout_alignStart="@id/person_gender" android:layout_below="@id/person_gender" android:layout_marginTop="8dp × </span> <p>{props.modalContent}</p> </div> </div> )} </" /> </RelativeLayout> ``` activity_main.xml: ```xml <?xml version="1.0" encoding="utf-8"?> <div> ); } export default Modal; ``` 这个组件接收两个props: - `buttonText`:按钮上RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout的文本 - `modalContent`:模态框中显示的内容 你可以在组件中这样使用这_height="match_parent"> <EditText android:id="@+id/search_box" android:layout_width="match_parent" 个组件: ```jsx import React from 'react'; import Modal from './Modal'; function App() { return ( <div android:layout_height="wrap_content" android:hint="Search by name" android:inputType="text" android:> <Modal buttonText="点击我" modalContent="这是一个模态框" /> </div> ); } export default App; ``` 当你点击“点击我”按钮时,会出现一个模态框,里面显示imeOptions="actionSearch" android:layout_margin="16dp" /> <Button android:id="@+id/search_button“这是一个模态框”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值