<center>this.props.children</center>

本文讲解了React中如何处理子组件的this.props.children属性,包括其数据类型及使用React.Children工具方法进行遍历和计数的方法。

this.props对象的属性与组件的属性一一对应,但是有个例外:this.props.children,他表示组件所有的子节点。

var NotesList = React.createClass({
  render: function() {
    return (
      <ol>
      {
        React.Children.map(this.props.children, function (child) {
          return <li>{child}</li>;
        })
      }
      </ol>
    );
  }
});
ReactDOM.render(
  <NotesList>
    <span>hello</span>
    <span>world</span>
  </NotesList>,
  document.body
);

上面代码的 NoteList 组件有两个 span 子节点,它们都可以通过 this.props.children 读取。这里需要注意, this.props.children 的值有三种可能:

如果当前组件没有子节点,它就是 undefined;
如果有一个子节点,数据类型是 object;
如果有多个子节点,数据类型就是 array
React 提供一个工具方法 React.Children来处理 this.props.children。我们可以用React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。

1.React.Children.map

object React.Children.map(object children, function fn [, object context])
在每一个直接子级(包含在children参数中的)上调用 fn 函数,此函数中的 this 指向 上下文。如果children 是一个内嵌的对象或者数组,它将被遍历:
不会传入容器对象到 fn 中。如果 children 参数是 null 或者 undefined,那么返回 null 或者 undefined 而不是一个空对象。

2.React.Children.forEach

React.Children.forEach(object children, function fn [, object context])
类似于React.Children.map(),但是不返回对象。

3.React.Children.count

number React.Children.count(object children)
返回 children 当中的组件总数,和传递给 map 或者forEach的回调函数的调用次数一致。

4.React.Children.only

object React.Children.only(object children)
返回 children 中仅有的子级。否则抛出异常。

转化为使用vue3 <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="shortcut icon" type="image/png" href="https://zctnet.top:666/favicon.png"> <title>Asgard在线服务器查询</title> <!-- 引入样式 --> <link rel="stylesheet" href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/theme-chalk/index.css"> <style> .userId { background-color: lightgray; width: auto; margin-left: 2%; margin-top: 5px; } .table-expand label { width: 90px; color: #99a9bf; } .table-expand .el-form-item { border: 1px solid blue; margin-right: 0; margin-bottom: 0; width: 80%; } el-table-column { border: 1px solid red; } </style> </head> <body> <div id="app"> <el-container> <el-header style="text-align: center;width: 100%;height: auto"> <h1>Asgard服务器状态查询</h1> <h4><a href="https://zctnet.top:666/">返回首页</a></h4> <h4><a href="https://zctnet.top:666/mcstatus">新版查询</a> | <a href="https://zctnet.top:666/Map">浏览地图</a> | <a href="https://play.mcmod.cn/sv20186263.html">百科点赞!</a> </h4> <el-row type="flex" justify="center" style="z-index: 100"> <el-col :span="20" style="border: 3px double grey;height: auto;padding-bottom: 1%"> <p> 目前总共有 {{ sum }} 名玩家在服务器中游玩 </p> <el-col style="height: auto;width: 100%"> <el-row> <div v-for="tableData in tableData"> <el-col :span="4" class="userId" v-for="player in tableData.players.sample"> <img width="32" height="32" :src='headApi+player.name'/> {{ player.name }} </el-col> </div> </el-row> </el-col> </el-col> </el-row> </el-header> <el-main> <el-row type="flex" justify="center"> <el-col :span="20"> <el-table :data="tableData" stripe fit :header-cell-style="{'text-align':'center'}" :cell-style="{'text-align':'center'}"> <el-table-column type="expand"> <i class="el-icon-caret-right"></i> <template slot-scope="props"> <el-table :data="props.row.players.sample" style="width: 90%;margin-left: 5%" row-key="id" lazy :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> <el-table-column width="64"> <template slot-scope="playerInfo"> <img width="32" height="32" :src='headApi+playerInfo.row.name' alt=""/> </template> </el-table-column> <el-table-column prop="name" width="720"> </el-table-column> </el-table> </template> </el-table-column> <el-table-column label="LOGO" width="128"> <template slot-scope="props"> <img width="64" height="64" :src='props.row.icon' alt=""/> </template> </el-table-column> <el-table-column prop="name" label="服号" width="60"> </el-table-column> <el-table-column prop="version.name" label="版本" width="90"> </el-table-column> <el-table-column label="在线玩家" width="150"> <template slot-scope="scope"> {{ scope.row.players.online }}/{{ scope.row.players.max }} </template> </el-table-column> <el-table-column label="服务器" width="128"> <template slot-scope="scope"> <div v-if="scope.row.players.max!==0"> <span>online</span> <i class="el-icon-success" style="color: green"></i> </div> <div v-else> <span>offline</span> <i class="el-icon-error" style="color: red"></i> </div> </template> </el-table-column> <el-table-column label="MOTD"> <template slot-scope="scope"> <div v-html="scope.row.motd"></div> </template> </el-table-column> </el-table> </el-col> </el-row> </el-main> </el-container> </div> <script src="https://cdn.jsdelivr.net/npm/axios@1.3.6/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script> <script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/element-ui/2.15.7/index.js"></script> <script> var app = new Vue({ el: '#app', data: { headApi: "https://mineskin.eu/avatar/", urls: [ 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25565&name=1服', 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25566&name=2服', 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25567&name=3服', 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25568&name=4服', 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25569&name=5服', 'https://zctnet.top:3001/server_query/mc?host=zctnet.top&port=25570&name=6服', 'https://zctnet.top:3001/server_query/mc?host=lt.zctnet.top&port=25565&name=7服', 'https://zctnet.top:3001/server_query/mc?host=jc.zctnet.top&port=25565&name=8服', 'https://zctnet.top:3001/server_query/mc?host=jc.zctnet.top&port=25566&name=9服', 'https://zctnet.top:3001/server_query/mc?host=jc.zctnet.top&port=25567&name=10服', 'https://zctnet.top:3001/server_query/mc?host=lt.zctnet.top&port=25566&name=11服', 'https://zctnet.top:3001/server_query/mc?host=api.coloryr.com&port=10005&name=15服' ], countUrl: 'https://zctnet.top:3000/count', loading: false, tableData: [] , key: 0,//手动刷新table drawer: false, drawerData: {}, drawerModNum: 0 }, created() { this.getInfos() setInterval(() => { this.getInfos() }, 60000);//一分钟一刷新 }, methods: { getInfos() { this.sum = 0; this.loading = true; this.tableData.length = 0; const requests = this.urls.map(url => fetch(url).then(response => { if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return response.json(); }).catch(error => { console.error(`请求失败: ${url}`, error); return {error: error.message}; // 返回错误信息以便后续处理 }) ); Promise.allSettled(requests) .then(results => { results.forEach((result, index) => { if (result.status === 'fulfilled') { this.sum += result.value.data.players.online; this.tableData.push(result.value.data); } console.log(result); }); this.loading = false; }); }, } }) </script> </body> </html>
11-02
dev.tmpl.html: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> <%= htmlWebpackPlugin.options.title %> </title> <link rel="stylesheet" href="//at.alicdn.com/t/font_437295_4qcjd3ysd4s.css" /> </head> <body> <div id="loaded"></div> <div id="s-loading"> <span style="font-size: 20px;"> <i></i> <i></i> <i></i> <i></i> </span> </div> <%= htmlWebpackPlugin.options.ref %> <div id="container"></div> <script type="text/javascript"> (function () { function showUpdateMsg() { setTimeout(() => { document.body.innerHTML = `<div style="text-align: center;padding-top: 20%;font-size: 20px;font-weight: bold"> 您正在使用的浏览器版本过低,将不能正常浏览和使用。 <div> <a href="https://www.microsoft.com/edge" target="_blank">使用 Microsoft Edge 浏览器</a> <div style="width:20px;"></div> <a href="https://www.google.cn/chrome/" target="_blank">使用 Google Chrome 浏览器</a> </div> </div>`; }, 1000); } var explorer = window.navigator.userAgent.toLowerCase(); // ie11及之前的 if (explorer.indexOf('msie') > -1 || explorer.indexOf('trident') > -1) { showUpdateMsg(); } // 目前只对有webkit内核标识的浏览器做判断,其它的不做判断 if (explorer.indexOf('applewebkit') > -1) { if (explorer.indexOf('chrome') > -1) { var browser = explorer.match(/chrome\/([\d.]+)/)[1]; var verArry = browser.split('.'); if (Number(verArry[0]) < 31) { showUpdateMsg(); } } } return false; })(); window.onload = function () { }; </script> <script src="https://cdn.staticfile.org/reqwest/2.0.5/reqwest.js"></script> <script src="https://cdn.staticfile.org/babel-polyfill/7.2.5/polyfill.js"></script> <script src="https://cdn.staticfile.org/react/16.8.6/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.8.6/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/antd/2.13.14/antd.js"></script> <script src="https://cdn.staticfile.org/react-router-dom/4.2.0/react-router-dom.js"></script> <script src="https://cdn.staticfile.org/react-router/4.2.0/react-router.js"></script> <script src="https://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script> <script src="https://cdn.staticfile.org/echarts/6.0.0/echarts.common.min.js"></script> <script src="//mapv.baidu.com/build/mapv.min.js"></script> <script src="https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.188/dist/mapvgl.min.js"></script> <script src="https://api.map.baidu.com/api?v=3.0&ak=1111"></script> <script src="https://api.map.baidu.com/library/LuShu/1.2/src/LuShu_min.js"></script> <script src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=1111"></script> <script src=" https://clms-dtd.oss-cn-beijing.aliyuncs.com/ws/js/CanvasLayer.js"></script> </body> </html> pages/antv/index: import React from 'react'; import 'styles/less/antv.less'; import { Form, Row, Col } from 'antd'; import moment from 'moment/moment'; import DateDayTime from './components/DateDayTime'; import BarChart from './components/Top3BarChart'; import LoadBarChart from './components/LoadBarChart'; import UnLoadBarChart from './components/UnLoadBarChart'; import PieChart from './components/PieChart'; import ScrollChart from './components/ScrollChart'; import HorizonBarChart from './components/HorizonBarChart'; import TimeLineChart from './components/TimeLineChart'; import ZylChart from './components/ZylChart'; import JiaoyiChart from './components/JiaoyiChart'; const demoImg = 'https://img.wuliu01.com/antv-carrier-img/map.png'; const wdjImg = 'https://img.wuliu01.com/antv-carrier-img/icon_wdj.png'; import { connect } from 'dva'; import Tools from 'utils'; import { setState } from '@antv/s2'; // import { GeoLocation } from "./components/GeoLocation"; moment.locale('zh-cn', { weekdays: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], }); class AntV extends React.Component { constructor(props) { super(props); // this.height = 240*document.body.clientHeight/900; this.state = { activeTab: 0, dateInfo: { date: moment().format('YYYY年MM月DD日'), day: moment.weekdays(moment().day()), time: moment().format('HH:mm:ss'), }, }; } componentDidMount() { this.GeoLocation(); // this.autoRefresh(); setInterval(() => { const value = this.state.activeTab === 0 ? 1 : 0; this.setState({ activeTab: value }); }, 3000); } codeByCityName(province, cityName) { const { dicModel: { citys }, } = this.props; // console.log('city:' + cityName); // console.log(citys); for (let i = 0; i < citys.length; i++) { if (citys[i].label === province) { if (citys[i].children) { for (let j = 0; j < citys[i].children.length; j++) { if (citys[i].children[j].label === cityName) { return citys[i].children[j].value; } } } return ''; } } return ''; } GeoLocation = () => { const _this = this; const { dispatch } = this.props; var map = new BMap.Map('ditu'); // var point = new BMapGL.Point(116.331398,39.897445); // map.centerAndZoom(point,12); var geolocation = new BMap.Geolocation(); geolocation.getCurrentPosition(function (r) { if (this.getStatus() == BMAP_STATUS_SUCCESS) { var mk = new BMap.Marker(r.point); map.addOverlay(mk); map.panTo(r.point); // console.log('您的位置:' + r.point.lng + ',' + r.point.lat); // 创建地理编码实例 var myGeo = new BMap.Geocoder(); // 根据坐标得到地址描述 myGeo.getLocation(new BMap.Point(r.point.lng, r.point.lat), function (result) { if (result) { // console.log(result); const code = _this.codeByCityName( result.addressComponents.province, result.addressComponents.city ); // console.log(code); if (code) { dispatch({ type: 'antvModel/getWeather', payload: { districtId: code, }, }); } } }); } else { console.log('failed' + this.getStatus()); } }); }; render() { // console.log('activeTab', this.state.activeTab); const { antvModel: { receiveGoodsTotal, tradeStatistics, waybillStatus, shipperGoodsTotal, waybillList, sendGoodsTotal, user, traffic, goodsType, weather, }, } = this.props; // const data = indexModel ? indexModel.citys : []; // useEffect(() => { // GeoLocation(); // props.dispatch({ // type: "antvModel/getAntvData", // }); // }, []); let totalAmount = 0; let minYear, maxYear; if (traffic) { for (let i = 0; i < traffic.length; i++) { totalAmount += traffic[i].totalAmount; const yyyymmArr = traffic[i]['months'].split('-'); traffic[i]['month'] = yyyymmArr[1] + '月'; if (i === 0) { minYear = yyyymmArr[0]; } else if (i === traffic.length - 1) { maxYear = yyyymmArr[0]; } } } let userArray = []; if (user && user.shipper) { userArray.push({ 用户类型: '货主', 用户数: user.shipper }); userArray.push({ 用户类型: '独立司机', 用户数: user.standAloneDriver }); userArray.push({ 用户类型: '车队司机', 用户数: user.captainDriver }); userArray.push({ 用户类型: '车队', 用户数: user.captainNum }); } return ( <Form className="App"> <div className="App-header"> <div className="App-header-left"> <div id="ditu" style={{ display: 'none' }}></div> <Col span={8} className="App-header-left-city"> {weather?.location?.city} </Col> <Col span={8} className="App-header-left-weather"> {weather?.now?.text} </Col> <Col span={8} className="App-header-left-temperature"> <img src={wdjImg} height={15} alt="" /> {weather?.forecasts?.[0]?.low}-{weather?.forecasts?.[0]?.high}℃ </Col> </div> <div span={8} className="App-title"> 物流临沂网络货运控制塔 </div> <DateDayTime /> </div> <Row className="App-body-top"> <Col span={6}> <Row> <Col className="chart-container" style={{ marginTop: 0 }}> <div className="container-title">货运总量</div> <div style={{ height: '4px' }}></div> <div className="summary-container"> <div className="summary-container-cell"> <div className="summary-container-cell-title">累计运量</div> <div className="summary-container-cell-value">{totalAmount} 吨</div> </div> <div className="summary-container-cell"> <div className="summary-container-cell-title">统计日期</div> <div className="summary-container-cell-value"> {minYear === maxYear ? '' : minYear + '-'} {maxYear}年 </div> </div> </div> {traffic && traffic.length > 0 && <TimeLineChart data={traffic} />} </Col> </Row> <Row> <Col className="chart-container"> <div className="container-title">货运类型占比</div> {goodsType && goodsType.length > 0 && <PieChart data={goodsType} />} </Col> </Row> {this.state.activeTab === 0 ? ( <Row className="load-active-container"> <div className="container-title"> <div className="active-title" onClick={() => setState({ activeTab: 0 })}> 装货区域 </div> <div className="inactive-title" onClick={() => setState({ activeTab: 1 })}> 卸货区域 </div> </div> {sendGoodsTotal && sendGoodsTotal.length > 0 && ( <LoadBarChart data={sendGoodsTotal} /> )} </Row> ) : ( <Row className="unload-active-container"> <div className="container-title"> <div className="inactive-title" onClick={() => setState({ activeTab: 0 })}> 装货区域 </div> <div className="active-title" onClick={() => setState({ activeTab: 1 })}> 卸货区域 </div> </div> {receiveGoodsTotal && receiveGoodsTotal.length > 0 && ( <UnLoadBarChart data={receiveGoodsTotal} /> )} </Row> )} </Col> <Col span={12} className="App-body-center"> <Row> <Col className="App-body-center-top"> <img src={demoImg} className="map" alt="" /> </Col> <div className="cars-info"> 在线车辆<span className="zyl-cell-content-amount-num">{Math.round(100+100*Math.random())}</span>台 </div> </Row> <Row className="App-body-center-bottom"> <Col span={12} className="chart-container" style={{ height: (240 * document.body.clientHeight) / 900, marginTop: 0 }} > <div className="container-title">装运量</div> <ZylChart waybillStatus={waybillStatus} /> </Col> <Col span={12} className="chart-container" style={{ height: (240 * document.body.clientHeight) / 900, marginTop: 0 }} > <div className="container-title">企业发运量Top3</div> {shipperGoodsTotal && shipperGoodsTotal.length > 0 && ( <BarChart data={shipperGoodsTotal} /> )} </Col> </Row> </Col> <Col span={6}> <Row> <Col className="chart-container" style={{ marginTop: 0 }}> <div className="container-title">交易额</div> <div style={{ height: '8px' }}></div> <JiaoyiChart tradeStatistics={tradeStatistics} /> </Col> </Row> <Row> <Col className="chart-container"> <div className="container-title">平台用户</div> <div style={{ height: '4px' }}></div> {userArray && userArray.length > 0 && <HorizonBarChart data={userArray} />} </Col> </Row> <Row> <Col className="chart-container" style={{ height: (240 * document.body.clientHeight) / 900 }} > <div className="container-title">实时运单</div> {waybillList && waybillList.length > 0 && <ScrollChart data={waybillList} />} </Col> </Row> </Col> </Row> <div style={{ height: '12px' }}></div> </Form> ); } } export default connect(({ antvModel, dicModel }) => ({ antvModel, dicModel, }))(AntV); 现在这里的地图是个图片,改成百度地图的MapVGL, 下面是百度地图的MapVGL示例参考: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>MapVGL</title> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"> <style> html, body { width: 100%; height: 100%; margin: 0; padding: 0; } #map_container { width: 100%; height: 100%; margin: 0; } </style> <script src="//api.map.baidu.com/api?v=1.0&type=webgl&ak=您的密钥"></script> <script src="//mapv.baidu.com/build/mapv.min.js"></script> <script src="static/common.js"></script> <script src="https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.189/dist/mapvgl.min.js"></script> <script src="https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.189/dist/mapvgl.threelayers.min.js"></script> </head> <body> <div id="map_container"></div> <script> /* global BMapGL */ /* global mapv */ /* global mapvgl */ /* global initMap */ /* global purpleStyle */ var map = initMap({ tilt: 41.8, heading: 0, center: [105.552849,28.847593], zoom: 5, style: purpleStyle }); var citys = [ '北京', '天津', '上海', '重庆', '石家庄', '太原', '呼和浩特', '哈尔滨', '长春', '沈阳', '济南', '南京', '合肥', '杭州', '南昌', '福州', '郑州', '武汉', '长沙', '广州', '南宁', '西安', '银川', '兰州', '西宁', '乌鲁木齐', '成都', '贵阳', '昆明', '拉萨', '海口' ]; var randomCount = 50; // 模拟的飞线的数量 var curve = new mapvgl.BezierCurve(); var data = []; // 构造数据 while (randomCount--) { var startPoint = mapv.utilCityCenter.getCenterByCityName(citys[parseInt(Math.random() * citys.length, 10)]); var endPoint = mapv.utilCityCenter.getCenterByCityName(citys[parseInt(Math.random() * citys.length, 10)]); var length = 0; var startPoint = map.lnglatToMercator(startPoint.lng, startPoint.lat); var endPoint = map.lnglatToMercator(endPoint.lng, endPoint.lat); curve.setOptions({ start: [startPoint[0], startPoint[1]], end: [endPoint[0], endPoint[1]] }); var curveModelData = curve.getPoints(60); data.push({ geometry: { type: 'LineString', coordinates: curveModelData }, properties: { count: Math.random() } }); } var view = new mapvgl.View({ // postProcessing: new mapvgl.PostProcessing({ // passes: [{ // name: 'unrealBloom', // threshold: 0.0, // strength: 1.5, // radius: 1.0 // }] // }), effects: [ new mapvgl.BrightEffect({ threshold: 0, blurSize: 2, clarity: 0.4 }), ], map: map }); var lineLayer = new mapvgl.LineTripLayer({ color: 'rgb(255, 255, 204)', // 飞线动画颜色 step: 0.3 }); view.addLayer(lineLayer); lineLayer.setData(data.map(item => { item.geometry.coordinates = item.geometry.coordinates.map(item => { item[2] += 3; return item; }); return item; })); var lineLayer = new mapvgl.SimpleLineLayer({ blend: 'lighter', color: 'rgb(255, 153, 0, 0.6)' // 飞线颜色 }); view.addLayer(lineLayer); lineLayer.setData(data); </script> </body> </html>
09-26
import React from 'react'; import type {FC} from 'react'; import {NavBar, TabBar} from 'antd-mobile'; import { Route, useLocation, useNavigate, MemoryRouter as Router, } from 'react-router-dom'; import { AppOutline, MessageOutline, UnorderedListOutline, UserOutline, } from 'antd-mobile-icons'; import {StyleSheet, Switch} from 'react-native'; const Bottom: FC = () => { const location = useLocation(); const {pathname} = location; const navigate = useNavigate(); const setRouteActive = (value: string) => { navigate(value); }; const tabs = [ { key: '/home', title: '首页', icon: <AppOutline />, }, { key: '/todo', title: '待办', icon: <UnorderedListOutline />, }, { key: '/message', title: '消息', icon: <MessageOutline />, }, { key: '/me', title: '我的', icon: <UserOutline />, }, ]; return ( <TabBar activeKey={pathname} onChange={value => setRouteActive(value)}> {tabs.map(item => ( <TabBar.Item key={item.key} icon={item.icon} title={item.title} /> ))} </TabBar> ); }; export default () => { return ( <Router initialEntries={['/home']}> <div style={styles.app}> <div style={styles.top}> <NavBar>配合路由使用</NavBar> </div> <div style={styles.body}> <Switch> <Route path="/home"> <Home /> </Route> <Route path="/todo"> <Todo /> </Route> <Route path="/message"> <Message /> </Route> <Route path="/me"> <PersonalCenter /> </Route> </Switch> </div> <div style={styles.bottom}> <Bottom /> </div> </div> </Router> ); }; function Home() { return <div>首页</div>; } function Todo() { return <div>待办</div>; } function Message() { return <div>消息</div>; } function PersonalCenter() { return <div>我的</div>; } const styles = StyleSheet.create({ app: { height: '100%', display: 'flex', flexDirection: 'column', }, top: { flex: 0, borderBottomWidth: 1, borderBottomColor: 'var(--adm-color-border)', }, body: { flex: 1, justifyContent: 'center', alignItems: 'center', }, bottom: { flex: 0, borderTopWidth: 1, borderTopColor: 'var(--adm-color-border)', }, }); 这个你检查下,这个好像没有导出,我准备直接在app.tsx引用,直接加载
03-08
<!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8" /> <title>3D 抽獎卡片展示</title> <style> body { margin: 0; height: 100vh; display: flex; justify-content: center; align-items: center; /* background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); */ background-color: black; font-family: Arial, sans-serif; overflow: hidden; } .stage { perspective: 1200px; width: 400px; height: 500px; position: relative; } .card-stack { width: 100%; height: 100%; position: absolute; transform-style: preserve-3d; } .card { position: absolute; padding: 20px; width: 320px; left: 40px; top: 50%; transform: translateY(-50%); border-radius: 20px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.35); display: flex; flex-direction: column; backface-visibility: hidden; border: 3px solid black; overflow: hidden; transition: transform 0.8s, opacity 0.8s, z-index 0.8s; } .card-header { font-size: 32px; font-weight: bold; padding: 10px; text-align: center; border-bottom: 2px solid rgba(255, 255, 255, 0.5); } .card-body { padding: 10px; font-size: 18px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 5px; } .name { background: rgba(255, 255, 255, 0.25); padding: 3px 8px; border: 2px solid #fff; border-radius: 6px; font-size: 16px; font-weight: 500; } </style> </head> <body> <div class="stage"> <div class="card-stack" id="stack"> <div class="card"> <div class="card-header">頭獎 - iPhone</div> <div class="card-body"> <span class="name">小明</span><span class="name">小華</span ><span class="name">小美</span> </div> </div> <div class="card"> <div class="card-header">二獎 - iPad</div> <div class="card-body"> <span class="name">小強</span><span class="name">阿玲</span ><span class="name">阿杰</span><span class="name">小魚</span ><span class="name">小熊</span> </div> </div> <div class="card"> <div class="card-header">三獎 - AirPods</div> <div class="card-body"> <span class="name">小玉</span><span class="name">小芳</span> </div> </div> <div class="card"> <div class="card-header">四獎 - Starbucks</div> <div class="card-body"> <span class="name">阿文</span><span class="name">阿國</span ><span class="name">小紅</span><span class="name">小綠</span ><span class="name">小藍</span><span class="name">小紫</span ><span class="name">小白</span> </div> </div> <div class="card"> <div class="card-header">五獎 - 全聯禮券</div> <div class="card-body"> <span class="name">小美</span><span class="name">阿東</span> </div> </div> <div class="card"> <div class="card-header">六獎 - 小確幸</div> <div class="card-body"> <span class="name">阿良良</span><span class="name">小黑</span ><span class="name">小黃</span><span class="name">阿秋黑</span ><span class="name">小光</span><span class="name">阿星</span ><span class="name">小藍</span><span class="name">小草</span> <span class="name">阿良</span><span class="name">小黑</span ><span class="name">小黃</span><span class="name">阿秋</span ><span class="name">小光</span><span class="name">阿星</span ><span class="name">小藍</span><span class="name">小草</span> <span class="name">阿良</span><span class="name">小黑</span ><span class="name">小黃</span><span class="name">阿秋</span ><span class="name">小光</span><span class="name">阿星</span ><span class="name">小藍</span><span class="name">小草</span> </div> </div> </div> </div> <script> // 可選配色板 const textColorPalette = [ 0x3498db, 0xe74c3c, 0x2ecc71, 0xf1c40f, 0x9b59b6, 0x1abc9c, ]; const colorPalette = [ "#3498db", "#e74c3c", "#2ecc71", "#f1c40f", "#9b59b6", "#1abc9c", ]; const stack = document.getElementById("stack"); let cards = Array.from(stack.children); // 計算文字顏色對比度 function getTextColor(bgColor) { // Remove '#' and parse hex string to RGB const hex = bgColor.replace("#", ""); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); // Calculate brightness using the same formula const brightness = 0.299 * r + 0.587 * g + 0.114 * b; // Return hex color string based on brightness return brightness > 186 ? "#000000" : "#ffffff"; } // 套用顏色 cards.forEach((card, i) => { card.style.background = colorPalette[i % colorPalette.length]; // colors[i].bg; card.style.color = getTextColor(colorPalette[i % colorPalette.length]); // colors[i].text; }); function rotateCards() { let first = cards.shift(); cards.push(first); cards.forEach((card, i) => { if (i === 0) { card.style.transform = "translateY(calc(-50% - 180px)) translateZ(-250px) rotateX(55deg) scale(0.8)"; card.style.opacity = 0.5; card.style.zIndex = 1; } else if (i === 1) { card.style.transform = "translateY(-50%) translateZ(0px) rotateX(0deg) scale(1.2)"; card.style.opacity = 1; card.style.zIndex = 10; } else if (i === 2) { card.style.transform = "translateY(calc(-50% + 180px)) translateZ(-250px) rotateX(-55deg) scale(0.8)"; card.style.opacity = 0.5; card.style.zIndex = 1; } else { card.style.transform = "translateY(-50%) translateZ(-600px) scale(0.6)"; card.style.opacity = 0; card.style.zIndex = 0; } }); } rotateCards(); setInterval(rotateCards, 3000); </script> </body> </html> 將上述單一html檔案修改成. vite + vie3 + ts 其他功能一率不變,資料動態傳入,使用 props
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值