├── .gitignore ├── LICENSE ├── demo ├── .expo-shared │ └── assets.json ├── .gitignore ├── App.js ├── app.json ├── assets │ ├── icon.png │ ├── smile.png │ ├── sound_off.png │ ├── splash.png │ └── view_off.png ├── babel.config.js ├── package.json └── yarn.lock ├── index.d.ts ├── index.js ├── lib ├── Toast.js └── ToastConstructer.js ├── package-lock.json ├── package.json ├── readme.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | web-report/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Barba Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /demo/App.js: -------------------------------------------------------------------------------- 1 | import { View, StyleSheet, Text, TouchableOpacity, Image, ScrollView } from 'react-native'; 2 | import React, { Component } from 'react'; 3 | import Toast, { Duration, Position } from 'react-native-mix-toast'; 4 | 5 | class MixToastExample extends Component { 6 | getToast1 = () => { 7 | Toast.show('Default Toast'); 8 | }; 9 | getToast10 = () => { 10 | Toast.show('View Off', 11 | { 12 | icon: require('./assets/view_off.png'), 13 | } 14 | ); 15 | }; 16 | getToast11 = () => { 17 | Toast.show('View Off', 18 | { 19 | icon: , 20 | } 21 | ); 22 | }; 23 | getToast2 = () => { 24 | Toast.show('Duration === LONG', { 25 | duration: Duration.LONG 26 | }); 27 | }; 28 | getToast3 = () => { 29 | Toast.show('Position === Top', { 30 | position: Position.TOP 31 | }); 32 | }; 33 | getToast4 = () => { 34 | Toast.show('Slide Animation', { 35 | animation: 'slide-right' 36 | }); 37 | }; 38 | getToast40 = () => { 39 | Toast.show('Scale Animation 2', { 40 | animation: 'scale' 41 | }); 42 | }; 43 | getToast5 = () => { 44 | Toast.show('Press To Hide', { 45 | hideOnPress: true, 46 | onPress: () => { 47 | alert('Press to hide the toast'); 48 | } 49 | }); 50 | }; 51 | getToast6 = () => { 52 | Toast.show('Lifecycle on Console', { 53 | onShow: () => { 54 | console.log('Toast Show Start'); 55 | }, 56 | onShown: () => { 57 | console.log('Toast Show End'); 58 | }, 59 | onHide: () => { 60 | console.log('Toast Hide Start'); 61 | }, 62 | onHidden: () => { 63 | console.log('Toast Hide End'); 64 | } 65 | }); 66 | }; 67 | _toast = null 68 | getToast60 = () => { 69 | this._toast = Toast.show('Touch PressToHide Will Hide The Toast', { 70 | duration: Duration.PERSIST 71 | }); 72 | }; 73 | getToast61 = () => { 74 | Toast.hide(this._toast) 75 | }; 76 | getToast62 = () => { 77 | this._toast = Toast.show('I am happy', { 78 | icon: , 79 | }); 80 | }; 81 | getToast63 = () => { 82 | Toast.update(this._toast, 'You are happy', { 83 | icon: , 84 | }) 85 | }; 86 | getToast7 = () => { 87 | Toast.show('Changed ToastSTyle', { 88 | toastStyle: { backgroundColor: 'yellowgreen' } 89 | }); 90 | }; 91 | getToast70 = () => { 92 | Toast.show('Changed TextSTyle', { 93 | textStyle: { color: 'yellowgreen', fontWeight: 'bold' } 94 | }); 95 | }; 96 | getToast71 = () => { 97 | Toast.show('Changed IconSTyle', { 98 | iconStyle: { backgroundColor: 'yellowgreen' }, 99 | icon: require('./assets/view_off.png'), 100 | }); 101 | }; 102 | getToast8 = () => { 103 | 104 | 105 | , 106 | Toast.show( 107 | 108 | 109 | you will smile 110 | 111 | ); 112 | }; 113 | getToast9 = () => { 114 | Toast.show( 115 | 116 | 117 | , 118 | { 119 | custom: true, 120 | animation: 'slide-bottom', 121 | opacity: 1 122 | } 123 | ); 124 | }; 125 | getToast10 = () => { 126 | Toast.show( 127 | 128 | 129 | , 130 | { 131 | position: 300, 132 | custom: true, 133 | animation: 'scale', 134 | opacity: 1, 135 | mask: true 136 | } 137 | ); 138 | }; 139 | 140 | _myToast = null 141 | _mySound = 50 142 | myToast1 = () => { 143 | this._myToast = Toast.show( 144 | this.iosNaitveSound(), 145 | { 146 | custom: true, 147 | animation: 'slide-top', 148 | opacity: 1, 149 | position: 40 150 | } 151 | ); 152 | } 153 | myToast2 = () => { 154 | if (this._mySound < 120) { this._mySound += 5 }; 155 | Toast.update( 156 | this._myToast, 157 | this.iosNaitveSound(), 158 | { 159 | custom: true, 160 | animation: 'slide-top', 161 | opacity: 1, 162 | position: 40 163 | } 164 | ); 165 | } 166 | iosNaitveSound = () => { 167 | return 168 | 169 | 170 | 铃声 171 | 172 | 173 | 174 | } 175 | 176 | myToast3 = () => { 177 | this._myToast = Toast.show( 178 | this.androidSound(), 179 | { 180 | custom: true, 181 | animation: 'slide-right', 182 | opacity: 1, 183 | position: Position.CENTER 184 | } 185 | ); 186 | } 187 | myToast4 = () => { 188 | if (this._mySound > 0) { this._mySound -= 5 }; 189 | Toast.update( 190 | this._myToast, 191 | this.androidSound(), 192 | { 193 | custom: true, 194 | animation: 'slide-right', 195 | opacity: 1, 196 | position: Position.CENTER 197 | } 198 | ); 199 | } 200 | androidSound = () => { 201 | return 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | } 210 | 211 | 212 | 213 | render () { 214 | return ( 215 | 216 | 217 | Mix Toast Demo 218 | 219 | Default Toast 220 | 221 | 222 | Duration Long 223 | 224 | 225 | Position Top 226 | 227 | 228 | 229 | Icon 230 | 231 | 232 | Icon2 233 | 234 | 235 | 236 | Slide Animation 237 | 238 | 239 | Scale Animation 240 | 241 | 242 | Press To Hide 243 | 244 | 245 | Lifecycle 246 | 247 | 248 | 249 | ToShow 250 | 251 | 252 | ToHide 253 | 254 | 255 | 256 | 257 | ToShow 258 | 259 | 260 | ToUpdate 261 | 262 | 263 | 264 | ToastStyle 265 | 266 | 267 | TextSTyle 268 | 269 | 270 | IconStyle 271 | 272 | 273 | Node Message 274 | 275 | 276 | Custom Node Message 277 | 278 | 279 | Node With Mask 280 | 281 | 282 | 283 | iOS Native 284 | 285 | Show Sound 286 | 287 | 288 | Update Sound 289 | 290 | Android Sound 291 | 292 | Show Sound 293 | 294 | 295 | Update Sound 296 | 297 | 298 | 299 | 300 | ); 301 | } 302 | } 303 | export default MixToastExample; 304 | 305 | const styles = StyleSheet.create({ 306 | container: { 307 | marginVertical: 100, 308 | justifyContent: 'center', 309 | alignItems: 'center' 310 | }, 311 | header: { 312 | margin: 10, 313 | fontSize: 42, 314 | fontWeight: 'bold', 315 | color: 'green' 316 | }, 317 | text: { 318 | margin: 10, 319 | fontSize: 30, 320 | fontWeight: 'bold', 321 | color: 'yellowgreen', 322 | }, 323 | custom: { 324 | backgroundColor: 'white', 325 | borderRadius: 10, 326 | padding: 10, 327 | shadowOpacity: 0.2, 328 | shadowColor: '#000', 329 | shadowRadius: 3, 330 | elevation: 3, 331 | }, 332 | iosNaitve: { 333 | width: 200, 334 | backgroundColor: '#f5f6f2', 335 | borderRadius: 50, 336 | padding: 12, 337 | shadowOpacity: 0.2, 338 | shadowColor: '#000', 339 | shadowRadius: 30, 340 | elevation: 3, 341 | alignItems: 'center', 342 | flexDirection: 'row' 343 | }, 344 | androidSound: { 345 | width: 375, 346 | justifyContent: 'center', 347 | alignItems: 'flex-end', 348 | shadowOpacity: 0.2, 349 | shadowColor: '#000', 350 | shadowRadius: 10, 351 | elevation: 2, 352 | } 353 | }); 354 | -------------------------------------------------------------------------------- /demo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "demo", 4 | "slug": "demo", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "web": { 23 | "favicon": "./assets/favicon.png" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Barba828/react-native-mix-toast/baff135f0afeda8a4e9f8ee7df2bb601d2fba2df/demo/assets/icon.png -------------------------------------------------------------------------------- /demo/assets/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Barba828/react-native-mix-toast/baff135f0afeda8a4e9f8ee7df2bb601d2fba2df/demo/assets/smile.png -------------------------------------------------------------------------------- /demo/assets/sound_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Barba828/react-native-mix-toast/baff135f0afeda8a4e9f8ee7df2bb601d2fba2df/demo/assets/sound_off.png -------------------------------------------------------------------------------- /demo/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Barba828/react-native-mix-toast/baff135f0afeda8a4e9f8ee7df2bb601d2fba2df/demo/assets/splash.png -------------------------------------------------------------------------------- /demo/assets/view_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Barba828/react-native-mix-toast/baff135f0afeda8a4e9f8ee7df2bb601d2fba2df/demo/assets/view_off.png -------------------------------------------------------------------------------- /demo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "react-native-mix-toast":"^0.1.1", 12 | "react-native-root-siblings": "^4.0.6", 13 | "expo": "~39.0.2", 14 | "expo-calendar": "~8.5.0", 15 | "expo-status-bar": "~1.0.2", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.2.tar.gz", 19 | "react-native-web": "~0.13.12" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "~7.9.0" 23 | }, 24 | "private": true 25 | } 26 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import Toast from './lib/Toast'; 2 | import { positions, durations } from './lib/ToastConstructer'; 3 | 4 | export { durations as Duration }; 5 | export { positions as Position }; 6 | export default Toast; 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Toast from './lib/Toast'; 2 | import { positions, durations } from './lib/ToastConstructer'; 3 | 4 | export { durations as Duration }; 5 | export { positions as Position }; 6 | export default Toast; 7 | -------------------------------------------------------------------------------- /lib/Toast.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import RootSiblings from 'react-native-root-siblings'; 3 | import ToastConstructer, { positions, durations } from './ToastConstructer'; 4 | 5 | class Toast extends Component { 6 | static displayName = 'Toast'; 7 | static propTypes = ToastConstructer.propTypes; 8 | static positions = positions; 9 | static durations = durations; 10 | 11 | constructor(props) { 12 | super(props); 13 | } 14 | 15 | //生产toast实例 16 | static show = ( 17 | message, 18 | options = { 19 | position: positions.BOTTOM, 20 | duration: durations.SHORT 21 | }, 22 | ) => { 23 | //new Toast对象实例 24 | let toast = new RootSiblings( 25 | ( 26 | { 29 | this.hide(toast) 30 | }} 31 | {...options}> 32 | {message} 33 | 34 | ), 35 | ); 36 | return toast 37 | }; 38 | 39 | static update = ( 40 | toast, 41 | message, 42 | options = { 43 | position: positions.BOTTOM, 44 | duration: durations.SHORT 45 | }, ) => { 46 | //判断toast类型 47 | if (toast instanceof RootSiblings) { 48 | toast.update( 49 | { 52 | this.hide(toast) 53 | }} 54 | {...options}> 55 | {message} 56 | 57 | ) 58 | } else { 59 | console.warn( 60 | `Toast.hide expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof toast}\` instead.`, 61 | ); 62 | } 63 | } 64 | 65 | //销毁toast实例 66 | static hide = toast => { 67 | //判断toast类型 68 | if (toast instanceof RootSiblings) { 69 | toast.destroy(); 70 | } else { 71 | console.warn( 72 | `Toast.hide expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof toast}\` instead.`, 73 | ); 74 | } 75 | }; 76 | } 77 | 78 | export { RootSiblings as Manager }; 79 | export default Toast; 80 | -------------------------------------------------------------------------------- /lib/ToastConstructer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | ViewPropTypes, 5 | StyleSheet, 6 | View, 7 | Text, 8 | Animated, 9 | Dimensions, 10 | TouchableWithoutFeedback, 11 | Easing, 12 | Keyboard, 13 | Image 14 | } from 'react-native'; 15 | const window = Dimensions.get('window'); 16 | /** 17 | * Toast展示位置 18 | * @TOP top:20% 19 | * @CENTER top:50% 20 | * @BOTTOM top:80% 21 | */ 22 | const positions = { 23 | TOP: window.height * 0.2, 24 | BOTTOM: window.height * 0.8, 25 | CENTER: window.height * 0.5, 26 | }; 27 | /** 28 | * Toast展示时间 29 | * @LONG 3500ms 30 | * @SHORT 2000ms 31 | * @PERSIST 永久展示 32 | */ 33 | const durations = { 34 | LONG: 3500, 35 | SHORT: 2000, 36 | PERSIST: Infinity, 37 | }; 38 | 39 | const MAX_WIDTH = 0.8; 40 | const MIN_WIDTH = 0.4; 41 | const ANIMATION_DURATION = 200; 42 | 43 | class ToastConstructor extends Component { 44 | constructor(props) { 45 | super(props); 46 | this.state = { 47 | visible: false, 48 | animatedValue: new Animated.Value(0),//动画状态值 49 | windowWidth: window.width, 50 | windowHeight: window.height, 51 | keyboardScreenY: window.height, 52 | }; 53 | //渐入动画 54 | this.animatedValueStart = Animated.timing(this.state.animatedValue, { 55 | toValue: 100, 56 | duration: ANIMATION_DURATION, 57 | easing: Easing.out(Easing.ease), 58 | useNativeDriver:false, 59 | }) 60 | //渐出动画 61 | this.animatedValueEnd = Animated.timing(this.state.animatedValue, { 62 | toValue: 0, 63 | duration: ANIMATION_DURATION, 64 | easing: Easing.in(Easing.ease), 65 | useNativeDriver:false, 66 | }) 67 | //展示定时器 68 | this.Timeout = null; 69 | this.ttt = null 70 | } 71 | 72 | componentDidMount () { 73 | // 响应式改变布局 74 | Dimensions.addEventListener('change', () => { 75 | this.setState({ 76 | windowWidth: window.width, 77 | windowHeight: window.height, 78 | }); 79 | }); 80 | this.props.keyboardAvoiding && Keyboard.addListener( 81 | 'keyboardDidChangeFrame', 82 | (endCoordinates) => { 83 | this.setState({ 84 | keyboardScreenY: endCoordinates.screenY, 85 | }); 86 | }, 87 | ); 88 | //new =>启动定时器开始show 89 | this.Timeout = setTimeout(() => this._show(), this.props.delay); 90 | } 91 | 92 | UNSAFE_componentWillReceiveProps (nextProps) { 93 | //update =>更新定时器和显示参数 94 | clearTimeout(this.Timeout); 95 | this.Timeout = setTimeout(() => { 96 | this._hide() 97 | }, nextProps.duration); 98 | } 99 | componentWillUnmount () { 100 | //hide =>清除定时器和监听 101 | clearTimeout(this.Timeout) 102 | Dimensions.removeEventListener('change'); 103 | Keyboard.removeListener('keyboardDidChangeFrame'); 104 | }; 105 | 106 | /** 107 | * 定义动画 108 | */ 109 | _animated () { 110 | let animate = { 111 | opacity: this.state.animatedValue.interpolate({ 112 | inputRange: [0, 100], 113 | outputRange: [0, this.props.opacity] 114 | }) 115 | } 116 | switch (this.props.animation) { 117 | case 'fade': 118 | break 119 | case 'slide-right': 120 | animate.transform = [{ 121 | translateX: this.state.animatedValue.interpolate({ 122 | inputRange: [0, 100], 123 | outputRange: [window.width / 2, 0] 124 | }) 125 | }] 126 | break 127 | case 'slide-left': 128 | animate.transform = [{ 129 | translateX: this.state.animatedValue.interpolate({ 130 | inputRange: [0, 100], 131 | outputRange: [-window.width / 2, 0] 132 | }) 133 | }] 134 | break 135 | case 'slide-bottom': 136 | animate.transform = [{ 137 | translateY: this.state.animatedValue.interpolate({ 138 | inputRange: [0, 100], 139 | outputRange: [this.props.position, 0] 140 | }) 141 | }] 142 | break 143 | case 'slide-top': 144 | animate.transform = [{ 145 | translateY: this.state.animatedValue.interpolate({ 146 | inputRange: [0, 100], 147 | outputRange: [-this.props.position, 0] 148 | }) 149 | }] 150 | break 151 | case 'scale': 152 | animate.transform = [{ 153 | scaleX: this.state.animatedValue.interpolate({ 154 | inputRange: [0, 100], 155 | outputRange: [0.1, 1] 156 | }), 157 | }, 158 | { 159 | scaleY: this.state.animatedValue.interpolate({ 160 | inputRange: [0, 100], 161 | outputRange: [0.1, 1] 162 | }) 163 | }] 164 | break 165 | case 'scale-vertical': 166 | animate.transform = [ 167 | { 168 | scaleY: this.state.animatedValue.interpolate({ 169 | inputRange: [0, 100], 170 | outputRange: [0.1, 1] 171 | }) 172 | }] 173 | break 174 | case 'scale-horizontal': 175 | animate.transform = [{ 176 | scaleX: this.state.animatedValue.interpolate({ 177 | inputRange: [0, 100], 178 | outputRange: [0.1, 1] 179 | }), 180 | }] 181 | break 182 | default: 183 | break; 184 | } 185 | return animate 186 | } 187 | 188 | /** 189 | * 定义遮罩层mask动画 190 | */ 191 | _maskAnimated () { 192 | return { 193 | backgroundColor: this.state.animatedValue.interpolate({ 194 | inputRange: [0, 100], 195 | outputRange: ["#0000", "#0003"] 196 | }) 197 | } 198 | } 199 | 200 | /** 201 | * 执行点击事件方法 202 | * @param {object} event 203 | */ 204 | _onPress (event) { 205 | this.props.onPress && this.props.onPress(event); 206 | this.props.hideOnPress && this._hide(); 207 | } 208 | 209 | /** 210 | * 展示toast 211 | */ 212 | _show () { 213 | clearTimeout(this.Timeout) 214 | this.props.onShow && this.props.onShow(); 215 | this.setState({ visible: true }, () => { 216 | this.animatedValueStart.start(() => { 217 | this.props.onShown && this.props.onShown(); 218 | //执行消失方法 219 | this.props.duration !== durations.PERSIST && (this.Timeout = setTimeout(() => { 220 | this._hide() 221 | }, this.props.duration)) 222 | }) 223 | }) 224 | } 225 | 226 | /** 227 | * 消失toast 228 | */ 229 | _hide () { 230 | clearTimeout(this.Timeout) 231 | this.props.onHide && this.props.onHide(); 232 | this.animatedValueEnd.start(() => { 233 | this.props.onHidden && this.props.onHidden(); 234 | this.setState({ visible: false }, () => { 235 | //销毁本组件 236 | this.props.destroyToast() 237 | }) 238 | }) 239 | } 240 | 241 | /** 242 | * toast内容 243 | */ 244 | renderContent () { 245 | const { position, custom, children, icon, toastStyle, textStyle, iconStyle, onPress, hideOnPress, touchable } = this.props; 246 | const { windowWidth, windowHeight, keyboardScreenY } = this.state; 247 | const heightRate = (keyboardScreenY / windowHeight)||1; 248 | let offset = { top: position * heightRate } 249 | return ( 250 | 251 | this._onPress(event)} pointerEvents="none"> 252 | 253 | {custom 254 | ? {typeof children === "string" 255 | ? {children} 256 | : (children)} 257 | 258 | : 262 | {typeof children === "string" 263 | ? ( 264 | {icon && typeof icon === 'number' 265 | ? 266 | : icon} 267 | {children} 268 | ) 269 | : (children)} 270 | } 271 | 272 | 273 | 274 | ) 275 | } 276 | 277 | render () { 278 | const { mask } = this.props; 279 | const { visible } = this.state; 280 | return visible 281 | ? mask 282 | ? 283 | {this.renderContent()} 284 | 285 | : this.renderContent() 286 | : null 287 | } 288 | } 289 | 290 | const styles = StyleSheet.create({ 291 | container: { 292 | position: 'absolute', 293 | left: 0, 294 | right: 0, 295 | justifyContent: 'center', 296 | alignItems: 'center', 297 | zIndex: 9999, 298 | }, 299 | toast: { 300 | padding: 8, 301 | paddingHorizontal: 12, 302 | backgroundColor: '#000', 303 | borderRadius: 6, 304 | minHeight: 44, 305 | }, 306 | mask: { 307 | width: window.width, 308 | height: window.height, 309 | position: 'absolute', 310 | backgroundColor: '#0003' 311 | }, 312 | view: { 313 | justifyContent: 'center', 314 | alignItems: 'center' 315 | }, 316 | text: { 317 | fontSize: 16, 318 | color: '#fff', 319 | textAlign: 'center', 320 | lineHeight: 28, 321 | }, 322 | icon: { 323 | width: 32, 324 | height: 32, 325 | resizeMode: 'contain' 326 | } 327 | }) 328 | 329 | /** 330 | * 定义参数类型 331 | */ 332 | ToastConstructor.propTypes = { 333 | ...ViewPropTypes, 334 | children: PropTypes.oneOfType([ 335 | PropTypes.string, 336 | PropTypes.node 337 | ]), 338 | duration: PropTypes.number, 339 | position: PropTypes.number, 340 | mask: PropTypes.bool, 341 | opacity: PropTypes.number, 342 | delay: PropTypes.number, 343 | animation: PropTypes.string, 344 | custom: PropTypes.bool, 345 | keyboardAvoiding: PropTypes.bool, 346 | hideOnPress: PropTypes.bool, 347 | toastStyle: ViewPropTypes.style, 348 | textStyle: Text.propTypes.style, 349 | onPress: PropTypes.func, 350 | onHide: PropTypes.func, 351 | onHidden: PropTypes.func, 352 | onShow: PropTypes.func, 353 | onShown: PropTypes.func, 354 | } 355 | 356 | /** 357 | * 默认参数值 358 | */ 359 | ToastConstructor.defaultProps = { 360 | duration: durations.SHORT, 361 | position: positions.BOTTOM, 362 | mask: false, 363 | custom: false, 364 | opacity: 0.8, 365 | delay: 0, 366 | hideOnPress: false, 367 | keyboardAvoiding: true, 368 | } 369 | 370 | export default ToastConstructor; 371 | export { positions, durations }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-mix-toast", 3 | "version": "0.0.8", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "react-native-root-siblings": { 8 | "version": "4.0.6", 9 | "resolved": "https://registry.npmjs.org/react-native-root-siblings/-/react-native-root-siblings-4.0.6.tgz", 10 | "integrity": "sha512-u/MaJLdD3bnshDFg8HWB0Xys1xlkQy6++3QthQlYw4kOFElobb6V1IIn5r46JvBTr9cf6dXsrFC4zPVXuwW1ww==", 11 | "requires": { 12 | "static-container": "^1.5.1" 13 | } 14 | }, 15 | "static-container": { 16 | "version": "1.5.1", 17 | "resolved": "https://registry.npmjs.org/static-container/-/static-container-1.5.1.tgz", 18 | "integrity": "sha512-OFChfLKIvSzaMA3otS5CEabJTIzHFPhMxogIT+io4F207PXTvS6woFyjXIyXyqMIYAhryePGeFZYC6uLcG1lpA==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-mix-toast", 3 | "version": "0.1.2", 4 | "description": "react-native toast with animation for iOS & Android", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "files": [ 10 | "lib", 11 | "index.js", 12 | "index.d.ts" 13 | ], 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "dependencies": { 18 | "react-native-root-siblings": "^4.0.6" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/Barba828/react-native-mix-toast.git" 23 | }, 24 | "keywords": [ 25 | "react-native", 26 | "toast", 27 | "android", 28 | "ios" 29 | ], 30 | "author": "Barba Lee", 31 | "license": "ISC", 32 | "bugs": { 33 | "url": "https://github.com/Barba828/react-native-mix-toast/issues" 34 | }, 35 | "homepage": "https://github.com/Barba828/react-native-mix-toast#readme", 36 | "devDependencies": { 37 | "tern": "^0.24.3" 38 | } 39 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # react-native-mix-toast 轻提示 2 | [![npm package](https://img.shields.io/npm/v/react-native-mix-toast)](https://www.npmjs.com/package/react-native-mix-toast) 3 | [![npm download](https://img.shields.io/npm/dy/react-native-mix-toast)](https://www.npmjs.com/package/react-native-mix-toast) 4 | [![license](https://img.shields.io/npm/l/react-native-mix-toast)](https://github.com/Barba828/react-native-mix-toast/blob/master/LICENSE) 5 | [![Stars](https://img.shields.io/github/stars/Barba828/react-native-mix-toast)](https://github.com/Barba828/react-native-mix-toast/stargazers) 6 | 7 | ## DESCRIBE 8 | Android和iOS平台通用的自定义Toast UI组件 9 | 10 | ## USAGE 11 | 12 | ### INSTALL 13 | ```shell 14 | npm install react-native-mix-toast 15 | ``` 16 | 17 | ### IMPORT 18 | ```js 19 | import Toast, { Duration, Position } from 'react-native-mix-toast'; 20 | ``` 21 | ### EXAMPLE 22 | ```js 23 | Toast.show('This is Toast', { 24 | duration: Duration.LONG, 25 | }); 26 | ``` 27 | 28 | ## FUNC 29 | 30 | ### show(content,{options}) 31 | 显示Toast 32 | 属性 | 说明 | 类型 | 默认值 33 | ----|-----|------|------ 34 | | content | Toast显示文本内容,可为`React.Node` | `string`、`node` | - | 35 | | {options} | Toast显示参数[API](##API) | `object` | - | 36 | 37 | ### update(toast,content,{options}) 38 | 更新Toast 39 | 属性 | 说明 | 类型 | 默认值 40 | ----|-----|------|------ 41 | | toast | 需要更新的toast | `object` | - | 42 | | content | 同`show()` | `string`、`node` | - | 43 | | {options} | 同`show()` | `object` | - | 44 | 45 | ### hide(toast) 46 | 可以主动调用关闭Toast 47 | 属性 | 说明 | 类型 | 默认值 48 | ----|-----|------|------ 49 | | toast | 需要更新的toast | `object` | - | 50 | 51 | ### EXAMPLE 52 | ```js 53 | // show() 54 | var myToast = Toast.show( 55 | 'This is Toast', 56 | { duration: Duration.PERSIST } 57 | ); 58 | // update() 59 | Toast.update( 60 | myToast, 61 | 'This is Updated Toast', 62 | { duration: Duration.PERSIST } 63 | ); 64 | // hide() 65 | Toast.hide(myToast) 66 | ``` 67 | 68 | ## API 69 | 属性 | 说明 | 类型 70 | ----|-----|------ 71 | | content | Toast显示文本内容,也可传入`React.Node` | `string`、`node` | 72 | | options | Toast显示设置 | `object` | 73 | 74 | ### OPTIONS 75 | 属性 | 说明 | 类型 | 默认值 76 | ----|-----|------|------ 77 | | duration | 显示时间,默认提供`LONG`,`SHORT`,`PERSIST` | `number` | `SHORT` | 78 | | position | 显示位置,默认提供`BOTTOM`,`TOP`,`CENTER` | `number` | `BOTTOM` | 79 | | mask | 遮罩层 | `bool` | `false` | 80 | | icon | 显示图标 | `number`、`node` | - | 81 | | opacity | 显示透明度 | `number` | `0.8` | 82 | | delay | 延时显示 | `number` | `0` | 83 | | animation | 渐入渐出动画,默认提供`fade`,`slide-right`,`slide-left`,`slide-bottom`,`slide-top`,`scale`,`scale-vertical`,`scale-horizontal` | `string` | `fade` | 84 | | custom | 完全自定义显示内容 | `boolean` | `false` | 85 | | keyboardAvoiding | 避免键盘遮挡 | `boolean` | `true` | 86 | | toastStyle| Toast自定义样式 | `object` | - | 87 | | textStyle | Toast文本自定义样式 | `object` | - | 88 | | iconStyle | Toast图标自定义样式 | `object` | - | 89 | | touchable | 可点击内容 | `object` | - | 90 | | hideOnPress | 点击取消显示 | `boolean` | `fasle` | 91 | | onPress | 点击Toast触发事件 | `function` | - | 92 | | onShow | 显示动画开始调用函数 | `function` | - | 93 | | onShown | 显示动画结束调用函数 | `function` | - | 94 | | onHide | 消失动画开始调用函数 | `function` | - | 95 | | onHidden | 消失动画结束调用函数 | `function` | - | 96 | 97 | ## DEMO 98 | ```shell 99 | cd demo 100 | npm install 101 | npm start 102 | ``` 103 | or 104 | 105 | [![Expo](https://img.shields.io/badge/expo-demo-blue)](https://expo.io/@barba-lee/demo) 106 | 107 | 108 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | acorn-loose@^6.0.0: 6 | version "6.1.0" 7 | resolved "https://registry.npm.taobao.org/acorn-loose/download/acorn-loose-6.1.0.tgz#3b2de5b3fc64f811c7b6c07cd9128d1476817f94" 8 | integrity sha1-Oy3ls/xk+BHHtsB82RKNFHaBf5Q= 9 | dependencies: 10 | acorn "^6.2.0" 11 | 12 | acorn-walk@^6.0.0: 13 | version "6.2.0" 14 | resolved "https://registry.npm.taobao.org/acorn-walk/download/acorn-walk-6.2.0.tgz?cache=0&sync_timestamp=1597235826369&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn-walk%2Fdownload%2Facorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" 15 | integrity sha1-Ejy487hMIXHx9/slJhWxx4prGow= 16 | 17 | acorn@^6.0.0, acorn@^6.2.0: 18 | version "6.4.1" 19 | resolved "https://registry.npm.taobao.org/acorn/download/acorn-6.4.1.tgz?cache=0&sync_timestamp=1597235823632&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" 20 | integrity sha1-Ux5Yuj9RudrLmmZGyk3r9bFMpHQ= 21 | 22 | balanced-match@^1.0.0: 23 | version "1.0.0" 24 | resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 25 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 26 | 27 | brace-expansion@^1.1.7: 28 | version "1.1.11" 29 | resolved "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 30 | integrity sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0= 31 | dependencies: 32 | balanced-match "^1.0.0" 33 | concat-map "0.0.1" 34 | 35 | concat-map@0.0.1: 36 | version "0.0.1" 37 | resolved "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 38 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 39 | 40 | core-util-is@~1.0.0: 41 | version "1.0.2" 42 | resolved "https://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 43 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 44 | 45 | enhanced-resolve@^2.2.2: 46 | version "2.3.0" 47 | resolved "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-2.3.0.tgz?cache=0&sync_timestamp=1600384940681&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fenhanced-resolve%2Fdownload%2Fenhanced-resolve-2.3.0.tgz#a115c32504b6302e85a76269d7a57ccdd962e359" 48 | integrity sha1-oRXDJQS2MC6Fp2Jp16V8zdli41k= 49 | dependencies: 50 | graceful-fs "^4.1.2" 51 | memory-fs "^0.3.0" 52 | object-assign "^4.0.1" 53 | tapable "^0.2.3" 54 | 55 | errno@^0.1.3: 56 | version "0.1.7" 57 | resolved "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" 58 | integrity sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg= 59 | dependencies: 60 | prr "~1.0.1" 61 | 62 | fs.realpath@^1.0.0: 63 | version "1.0.0" 64 | resolved "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 65 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 66 | 67 | glob@^7.1.1: 68 | version "7.1.6" 69 | resolved "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz?cache=0&sync_timestamp=1586263931536&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fglob%2Fdownload%2Fglob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 70 | integrity sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY= 71 | dependencies: 72 | fs.realpath "^1.0.0" 73 | inflight "^1.0.4" 74 | inherits "2" 75 | minimatch "^3.0.4" 76 | once "^1.3.0" 77 | path-is-absolute "^1.0.0" 78 | 79 | graceful-fs@^4.1.2: 80 | version "4.2.4" 81 | resolved "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" 82 | integrity sha1-Ila94U02MpWMRl68ltxGfKB6Kfs= 83 | 84 | inflight@^1.0.4: 85 | version "1.0.6" 86 | resolved "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 87 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 88 | dependencies: 89 | once "^1.3.0" 90 | wrappy "1" 91 | 92 | inherits@2, inherits@~2.0.3: 93 | version "2.0.4" 94 | resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 95 | integrity sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w= 96 | 97 | isarray@~1.0.0: 98 | version "1.0.0" 99 | resolved "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 100 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 101 | 102 | memory-fs@^0.3.0: 103 | version "0.3.0" 104 | resolved "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" 105 | integrity sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA= 106 | dependencies: 107 | errno "^0.1.3" 108 | readable-stream "^2.0.1" 109 | 110 | minimatch@^3.0.3, minimatch@^3.0.4: 111 | version "3.0.4" 112 | resolved "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz?cache=0&sync_timestamp=1597044082534&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fminimatch%2Fdownload%2Fminimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 113 | integrity sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM= 114 | dependencies: 115 | brace-expansion "^1.1.7" 116 | 117 | object-assign@^4.0.1: 118 | version "4.1.1" 119 | resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 120 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 121 | 122 | once@^1.3.0: 123 | version "1.4.0" 124 | resolved "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 125 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 126 | dependencies: 127 | wrappy "1" 128 | 129 | path-is-absolute@^1.0.0: 130 | version "1.0.1" 131 | resolved "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 132 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 133 | 134 | process-nextick-args@~2.0.0: 135 | version "2.0.1" 136 | resolved "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 137 | integrity sha1-eCDZsWEgzFXKmud5JoCufbptf+I= 138 | 139 | prr@~1.0.1: 140 | version "1.0.1" 141 | resolved "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" 142 | integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= 143 | 144 | react-native-root-siblings@^4.0.6: 145 | version "4.0.6" 146 | resolved "https://registry.npm.taobao.org/react-native-root-siblings/download/react-native-root-siblings-4.0.6.tgz#6dd7eedb725faacd7ba19c159dd279cf2e6d8476" 147 | integrity sha1-bdfu23Jfqs17oZwVndJ5zy5thHY= 148 | dependencies: 149 | static-container "^1.5.1" 150 | 151 | readable-stream@^2.0.1: 152 | version "2.3.7" 153 | resolved "https://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 154 | integrity sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c= 155 | dependencies: 156 | core-util-is "~1.0.0" 157 | inherits "~2.0.3" 158 | isarray "~1.0.0" 159 | process-nextick-args "~2.0.0" 160 | safe-buffer "~5.1.1" 161 | string_decoder "~1.1.1" 162 | util-deprecate "~1.0.1" 163 | 164 | resolve-from@2.0.0: 165 | version "2.0.0" 166 | resolved "https://registry.npm.taobao.org/resolve-from/download/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" 167 | integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= 168 | 169 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 170 | version "5.1.2" 171 | resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 172 | integrity sha1-mR7GnSluAxN0fVm9/St0XDX4go0= 173 | 174 | static-container@^1.5.1: 175 | version "1.5.1" 176 | resolved "https://registry.npm.taobao.org/static-container/download/static-container-1.5.1.tgz#9d7a94e04dea864539a7b6a1304843ada740dc19" 177 | integrity sha1-nXqU4E3qhkU5p7ahMEhDradA3Bk= 178 | 179 | string_decoder@~1.1.1: 180 | version "1.1.1" 181 | resolved "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 182 | integrity sha1-nPFhG6YmhdcDCunkujQUnDrwP8g= 183 | dependencies: 184 | safe-buffer "~5.1.0" 185 | 186 | tapable@^0.2.3: 187 | version "0.2.9" 188 | resolved "https://registry.npm.taobao.org/tapable/download/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" 189 | integrity sha1-ry2LvJsE907hevK02QSPgHrNGKg= 190 | 191 | tern@^0.24.3: 192 | version "0.24.3" 193 | resolved "https://registry.npm.taobao.org/tern/download/tern-0.24.3.tgz#da0d9118490c15af4b1c94553d5f0fafc77a0977" 194 | integrity sha1-2g2RGEkMFa9LHJRVPV8Pr8d6CXc= 195 | dependencies: 196 | acorn "^6.0.0" 197 | acorn-loose "^6.0.0" 198 | acorn-walk "^6.0.0" 199 | enhanced-resolve "^2.2.2" 200 | glob "^7.1.1" 201 | minimatch "^3.0.3" 202 | resolve-from "2.0.0" 203 | 204 | util-deprecate@~1.0.1: 205 | version "1.0.2" 206 | resolved "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 207 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 208 | 209 | wrappy@1: 210 | version "1.0.2" 211 | resolved "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 212 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 213 | --------------------------------------------------------------------------------