Tooltip with Transparent Shadow

本文介绍了一种使用CSS和JavaScript实现的带有透明阴影效果的提示框,通过调整样式和脚本来确保提示框在不同浏览器中正确显示,并覆盖下拉菜单等元素。

Put this into your <HEAD> section

	<style type="text/css">
	/* YOU CAN REMOVE THIS PART */
	body{
		background-image:url('../../images/heading3.gif');
		background-repeat:no-repeat;
		padding-top:85px;	
		font-family: Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif;
		font-size:0.9em;
		line-height:130%;

	}
	h1{
		line-height:130%;
	}
	a{
		color: #D60808;
		text-decoration:none;
		font-weight:bold;
	}
	a:hover{
		border-bottom:1px dotted #317082;
		color: #307082;
	}
   		
	/* END PART YOU CAN REMOVE */
	
	
	#dhtmlgoodies_tooltip{
		background-color:#EEE;
		border:1px solid #000;
		position:absolute;
		display:none;
		z-index:20000;
		padding:2px;
		font-size:0.9em;
		-moz-border-radius:6px;	/* Rounded edges in Firefox */
		font-family: "Trebuchet MS", "Lucida Sans Unicode", Arial, sans-serif;
		
	}
	#dhtmlgoodies_tooltipShadow{
		position:absolute;
		background-color:#555;
		display:none;
		z-index:10000;
		opacity:0.7;
		filter:alpha(opacity=70);
		-khtml-opacity: 0.7;
		-moz-opacity: 0.7;
		-moz-border-radius:6px;	/* Rounded edges in Firefox */
	}
	</style>
	<SCRIPT type="text/javascript">
	/************************************************************************************************************
	(C) www.dhtmlgoodies.com, October 2005
	
	This is a script from www.dhtmlgoodies.com. You will find this and a lot of other scripts at our website.	
	
	Updated:	
		March, 11th, 2006 - Fixed positioning of tooltip when displayed near the right edge of the browser.
		April, 6th 2006, Using iframe in IE in order to make the tooltip cover select boxes.
		
	Terms of use:
	You are free to use this script as long as the copyright message is kept intact. However, you may not
	redistribute, sell or repost it without our permission.
	
	Thank you!
	
	www.dhtmlgoodies.com
	Alf Magne Kalleland
	
	************************************************************************************************************/	
	var dhtmlgoodies_tooltip = false;
	var dhtmlgoodies_tooltipShadow = false;
	var dhtmlgoodies_shadowSize = 4;
	var dhtmlgoodies_tooltipMaxWidth = 200;
	var dhtmlgoodies_tooltipMinWidth = 100;
	var dhtmlgoodies_iframe = false;
	var tooltip_is_msie = (navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('opera')==-1 && document.all)?true:false;
	function showTooltip(e,tooltipTxt)
	{
		
		var bodyWidth = Math.max(document.body.clientWidth,document.documentElement.clientWidth) - 20;
	
		if(!dhtmlgoodies_tooltip){
			dhtmlgoodies_tooltip = document.createElement('DIV');
			dhtmlgoodies_tooltip.id = 'dhtmlgoodies_tooltip';
			dhtmlgoodies_tooltipShadow = document.createElement('DIV');
			dhtmlgoodies_tooltipShadow.id = 'dhtmlgoodies_tooltipShadow';
			
			document.body.appendChild(dhtmlgoodies_tooltip);
			document.body.appendChild(dhtmlgoodies_tooltipShadow);	
			
			if(tooltip_is_msie){
				dhtmlgoodies_iframe = document.createElement('IFRAME');
				dhtmlgoodies_iframe.frameborder='5';
				dhtmlgoodies_iframe.style.backgroundColor='#FFFFFF';
				dhtmlgoodies_iframe.src = '#'; 	
				dhtmlgoodies_iframe.style.zIndex = 100;
				dhtmlgoodies_iframe.style.position = 'absolute';
				document.body.appendChild(dhtmlgoodies_iframe);
			}
			
		}
		
		dhtmlgoodies_tooltip.style.display='block';
		dhtmlgoodies_tooltipShadow.style.display='block';
		if(tooltip_is_msie)dhtmlgoodies_iframe.style.display='block';
		
		var st = Math.max(document.body.scrollTop,document.documentElement.scrollTop);
		if(navigator.userAgent.toLowerCase().indexOf('safari')>=0)st=0; 
		var leftPos = e.clientX + 10;
		
		dhtmlgoodies_tooltip.style.width = null;	// Reset style width if it's set 
		dhtmlgoodies_tooltip.innerHTML = tooltipTxt;
		dhtmlgoodies_tooltip.style.left = leftPos + 'px';
		dhtmlgoodies_tooltip.style.top = e.clientY + 10 + st + 'px';

		
		dhtmlgoodies_tooltipShadow.style.left =  leftPos + dhtmlgoodies_shadowSize + 'px';
		dhtmlgoodies_tooltipShadow.style.top = e.clientY + 10 + st + dhtmlgoodies_shadowSize + 'px';
		
		if(dhtmlgoodies_tooltip.offsetWidth>dhtmlgoodies_tooltipMaxWidth){	/* Exceeding max width of tooltip ? */
			dhtmlgoodies_tooltip.style.width = dhtmlgoodies_tooltipMaxWidth + 'px';
		}
		
		var tooltipWidth = dhtmlgoodies_tooltip.offsetWidth;		
		if(tooltipWidth<dhtmlgoodies_tooltipMinWidth)tooltipWidth = dhtmlgoodies_tooltipMinWidth;
		
		
		dhtmlgoodies_tooltip.style.width = tooltipWidth + 'px';
		dhtmlgoodies_tooltipShadow.style.width = dhtmlgoodies_tooltip.offsetWidth + 'px';
		dhtmlgoodies_tooltipShadow.style.height = dhtmlgoodies_tooltip.offsetHeight + 'px';		
		
		if((leftPos + tooltipWidth)>bodyWidth){
			dhtmlgoodies_tooltip.style.left = (dhtmlgoodies_tooltipShadow.style.left.replace('px','') - ((leftPos + tooltipWidth)-bodyWidth)) + 'px';
			dhtmlgoodies_tooltipShadow.style.left = (dhtmlgoodies_tooltipShadow.style.left.replace('px','') - ((leftPos + tooltipWidth)-bodyWidth) + dhtmlgoodies_shadowSize) + 'px';
		}
		
		if(tooltip_is_msie){
			dhtmlgoodies_iframe.style.left = dhtmlgoodies_tooltip.style.left;
			dhtmlgoodies_iframe.style.top = dhtmlgoodies_tooltip.style.top;
			dhtmlgoodies_iframe.style.width = dhtmlgoodies_tooltip.offsetWidth + 'px';
			dhtmlgoodies_iframe.style.height = dhtmlgoodies_tooltip.offsetHeight + 'px';
		
		}
				
	}
	
	function hideTooltip()
	{
		dhtmlgoodies_tooltip.style.display='none';
		dhtmlgoodies_tooltipShadow.style.display='none';		
		if(tooltip_is_msie)dhtmlgoodies_iframe.style.display='none';		
	}
	
	</SCRIPT>	

 

Put this into your <BODY> section

<h1>Roll your mouse over the links in the text below</h1>	
<P><a href="#" onmouseout="hideTooltip()" onmouseover="showTooltip(event,'This is a tooltip with a transparent shadow effect. Can you see the letters underneath the shadow?');return false">Roll over me</a></P>

 

<template> <div class="search-container"> <div class="input-with-icon"> <bc-icon name="ele-Search" class="icon" size="16"></bc-icon> <input ref="inputRef" v-model="searchQuery" @input="searchDrugs" placeholder="药品名称/物质名称" class="search-input" clearable /> </div> <!-- PopView 弹出表格 --> <teleport to="body"> <div v-if="showResults" class="popup-table" :style="{ top: popupPosition.top + 'px', left: popupPosition.left - 30 + 'px', width: popupPosition.width + 'px', }" > <!-- 药品搜索结果列表 --> <el-table :data="drugResults" border :height="300" style="width: 100%" @row-click="handleRowClick" :row-style="{ cursor: 'pointer' }" > <el-table-column prop="commodity_no" label="商品编码" width="100" :show-overflow-tooltip="true" cell-class-name="nowrap-cell" ></el-table-column> <el-table-column prop="commodity_name" label="药名" :show-overflow-tooltip="true" cell-class-name="nowrap-cell" ></el-table-column> <el-table-column prop="size" label="规格" width="115" :show-overflow-tooltip="true" cell-class-name="nowrap-cell" ></el-table-column> <el-table-column prop="manufacturer_name" label="厂家" :show-overflow-tooltip="true" cell-class-name="nowrap-cell" ></el-table-column> <el-table-column prop="remark" label="备注" width="120" :show-overflow-tooltip="true" cell-class-name="nowrap-cell" ></el-table-column> </el-table> </div> </teleport> </div> </template> <script setup lang="ts"> import { ref, onMounted, onBeforeUnmount, watch } from 'vue'; import { HsMessage } from 'hs-admin-ui'; import { DrugItem } from '../../type'; import { useOtherStorageController } from '/@/api'; const searchQuery = ref(''); const drugResults = ref<DrugItem[]>([]); const showResults = ref(false); const inputRef = ref<HTMLInputElement | null>(null); const popupPosition = ref({ top: 0, left: 0, width: 0 }); const { getListPageGoodseatCommodity } = useOtherStorageController(); // 在营商品 const props = defineProps({ //入库仓库id in_repertory_id: { type: String, }, //出库商品ID out_repertory_id: { type: String, }, }); // const emit = defineEmits<{ (e: 'add-drug', drug: DrugItem): void; }>(); // 更新弹框位置 const updatePopupPosition = () => { if (inputRef.value) { const rect = inputRef.value.getBoundingClientRect(); popupPosition.value = { top: rect.bottom + window.scrollY, left: rect.left + window.scrollX, width: 740, }; } }; // 模拟搜索 const searchDrugs = async () => { if (!searchQuery.value.trim()) { showResults.value = false; emit('add-drug'); return; } let results: any = await getListPageGoodseatCommodity({ pageSize: -1, qry_like_info: searchQuery.value || '', }); drugResults.value = results.data.customPageList; showResults.value = true; updatePopupPosition(); }; // 添加药品 const handleRowClick = (row: DrugItem) => { emit('add-drug', row); showResults.value = false; // searchQuery.value = ''; }; // 点击外部关闭弹窗 const handleClickOutside = (e: MouseEvent) => { if (inputRef.value && !inputRef.value.contains(e.target as Node)) { showResults.value = false; } }; // 窗口变化时更新弹框位置 const handleResize = () => { if (showResults.value) { updatePopupPosition(); } }; onMounted(() => { document.addEventListener('click', handleClickOutside); window.addEventListener('resize', handleResize); }); onBeforeUnmount(() => { document.removeEventListener('click', handleClickOutside); window.removeEventListener('resize', handleResize); }); watch(showResults, (isVisible) => { if (isVisible) { updatePopupPosition(); } }); </script> <style scoped> .search-container { position: relative; width: 400px; padding: 10px 12px; .input-with-icon { position: relative; display: flex; align-items: center; border: 1px solid #ccc; background-color: #fff; border-radius: 4px; padding-left: 8px; width: 400px; height: 36px; box-sizing: border-box; .icon { margin-right: 8px; color: #999; } input { flex: 1; border: none; outline: none; background: transparent; font-size: 14px; height: 100%; &:focus { outline: none; } } } } .popup-table { position: absolute; border: 1px solid #ccc; background: #fff; z-index: 10000; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); max-height: 300px; overflow: auto; min-width: 600px; .nowrap-cell { white-space: nowrap; } } </style> 优化下代码,组块化编程,修改完提供完整代码
08-01
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import docx from docx.shared import Pt, Inches, RGBColor from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import os import base64 from io import BytesIO # 1. 数据处理与分析 # 加载数据 tips = pd.read_csv('tips.csv') # 计算小费比例 tips['tip_percentage'] = (tips['tip'] / tips['total_bill']) * 100 # 2. 生成可视化图表 plt.figure(figsize=(10, 6)) sns.scatterplot(data=tips, x='total_bill', y='tip', hue='time') plt.title('消费金额与小费关系分析', fontsize=14) plt.xlabel('总消费金额(美元)', fontsize=12) plt.ylabel('小费金额(美元)', fontsize=12) plt.grid(True) plt.tight_layout() plt.savefig('scatter_plot.png') plt.close() plt.figure(figsize=(8, 6)) sns.barplot(data=tips, x='sex', y='tip_percentage', ci=None) plt.title('不同性别小费比例对比', fontsize=14) plt.xlabel('性别', fontsize=12) plt.ylabel('小费比例(%)', fontsize=12) plt.grid(axis='y') plt.tight_layout() plt.savefig('gender_plot.png') plt.close() plt.figure(figsize=(10, 6)) sns.boxplot(data=tips, x='day', y='tip_percentage', hue='smoker') plt.title('不同日期吸烟与非吸烟顾客小费比例分布', fontsize=14) plt.xlabel('星期', fontsize=12) plt.ylabel('小费比例(%)', fontsize=12) plt.grid(axis='y') plt.tight_layout() plt.savefig('box_plot.png') plt.close() # 3. 创建Word实验报告 doc = docx.Document() # 设置文档默认字体 style = doc.styles['Normal'] font = style.font font.name = '宋体' font.size = Pt(12) # 标题 title = doc.add_heading('智能终端开发与数据可视化', level=0) title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER run = title.runs[0] run.font.name = '宋体' run.font.size = Pt(16) run.font.bold = True subtitle = doc.add_heading('课程报告', level=1) subtitle.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER run = subtitle.runs[0] run.font.name = '宋体' run.font.size = Pt(14) # 学生信息 doc.add_paragraph('姓名:徐丹晨') doc.add_paragraph('学号:2022326603023') doc.add_paragraph() # 一、概述 doc.add_heading('一、概述', level=1) overview = ( "数据可视化是通过图形化手段将复杂数据转化为直观图表的过程,帮助人们快速理解数据中的模式、" "趋势和异常。在本次课程中,我使用Python的Matplotlib和Seaborn库进行数据分析与可视化," "并结合HTML/CSS/JavaScript和ECharts库实现了网页端的数据大屏展示。" "本报告基于餐厅小费数据集(tips.csv)进行分析,该数据集包含账单金额、小费金额、顾客性别、" "是否吸烟、星期几、用餐时段和用餐人数等信息。" ) doc.add_paragraph(overview) doc.add_paragraph() # 二、案例分析 doc.add_heading('二、案例分析', level=1) # 图1 doc.add_heading('图1:消费金额与小费关系分析', level=2) doc.add_picture('scatter_plot.png', width=Inches(5)) analysis1 = ( "该散点图展示了总消费金额与小费金额之间的关系。从图中可以看出:\n" "1. 晚餐时段(蓝色点)的小费普遍高于午餐时段(橙色点)\n" "2. 总消费金额越高,小费金额也倾向于增加,两者存在正相关关系\n" "3. 存在一些异常点,如高消费但低小费的订单\n" "结论:顾客在晚餐时更慷慨,且小费金额与消费金额呈正比关系。" ) doc.add_paragraph(analysis1) doc.add_paragraph() # 图2 doc.add_heading('图2:不同性别小费比例对比', level=2) doc.add_picture('gender_plot.png', width=Inches(5)) analysis2 = ( "此柱状图比较了不同性别顾客给出的小费比例。分析显示:\n" "1. 男性顾客的小费比例(约16.7%)略高于女性顾客(约15.3%)\n" "2. 性别对小费比例的影响较小,差异不到1.5个百分点\n" "3. 其他因素(如消费金额、用餐时段)可能比性别影响更大\n" "结论:性别对小费比例的影响较小,不应作为预测小费的主要因素。" ) doc.add_paragraph(analysis2) doc.add_paragraph() # 图3 doc.add_heading('图3:不同日期吸烟与非吸烟顾客小费比例分布', level=2) doc.add_picture('box_plot.png', width=Inches(5)) analysis3 = ( "该箱线图展示了不同日期吸烟与非吸烟顾客的小费比例分布:\n" "1. 周六吸烟顾客(Yes-Sat)的小费比例分布范围最广\n" "2. 周日非吸烟顾客(No-Sun)的小费比例中位数最高\n" "3. 周四和周五的数据点较少,分布范围相对集中\n" "4. 非吸烟顾客的小费比例整体更稳定\n" "结论:周末吸烟顾客的小费行为波动较大,非吸烟顾客更稳定。" ) doc.add_paragraph(analysis3) doc.add_paragraph() # 三、网页端展示 doc.add_heading('三、网页端展示', level=1) web_intro = ( "基于分析结果,我设计了一个交互式数据大屏,使用ECharts实现以下可视化:\n" "1. 消费金额与小费关系散点图\n" "2. 不同性别小费比例柱状图\n" "3. 不同时段用餐人数分布图\n" "4. 小费比例分布箱线图\n" "5. 不同日期小费金额雷达图\n" "6. 吸烟与非吸烟顾客比例饼图\n\n" "网页设计特点:\n" "- 深色背景搭配渐变色,增强数据可视化效果\n" "- 响应式布局,适配不同屏幕尺寸\n" "- 悬浮提示交互,显示详细数据\n" "- 关键指标统计面板,突出核心数据" ) doc.add_paragraph(web_intro) doc.add_paragraph() # 添加网页截图占位符 doc.add_paragraph("图4:网页端数据大屏展示效果") doc.add_paragraph("(实际运行网页文件查看完整效果)") # 四、总结与感悟 doc.add_heading('四、总结与感悟', level=1) conclusion = ( "通过本课程的学习,我掌握了数据可视化的核心理论和实践技能:\n\n" "1. 数据分析能力:学会了使用Pandas进行数据清洗和特征工程,发现数据中的模式和关联\n" "2. 可视化技术:掌握了Matplotlib、Seaborn和ECharts的使用,能根据数据类型选择合适的图表\n" "3. 设计原则:理解了色彩搭配、布局设计和信息层次的重要性\n" "4. 网页开发:学会了将可视化结果整合到网页中,创建交互式数据大屏\n\n" "在本次大作业中,我深刻体会到:\n" "- 数据可视化是连接数据和决策的桥梁,能将复杂信息转化为直观洞察\n" "- 优秀的可视化需要平衡准确性、美观性和功能性\n" "- 小费行为受多种因素影响,其中消费金额和用餐时段的影响最显著\n\n" "未来学习方向:\n" "- 深入学习D3.js等高级可视化库\n" "- 探索大数据实时可视化技术\n" "- 研究可视化在人工智能解释性中的应用" ) doc.add_paragraph(conclusion) # 保存Word文档 doc.save('2022326603023-徐丹晨-数据可视化大作业报告.docx') # 4. 生成网页大屏 html_content = """ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>餐厅小费数据分析大屏</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Microsoft YaHei', sans-serif; } body { background: linear-gradient(135deg, #1a2a6c, #2c3e50); color: #ecf0f1; padding: 20px; min-height: 100vh; } .header { text-align: center; padding: 20px 0; margin-bottom: 30px; background: rgba(0, 0, 0, 0.3); border-radius: 15px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.1); } .header h1 { font-size: 2.8rem; margin-bottom: 10px; background: linear-gradient(90deg, #4facfe, #00f2fe); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .header p { font-size: 1.2rem; color: #bdc3c7; } .dashboard { display: grid; grid-template-columns: repeat(2, 1fr); gap: 25px; max-width: 1800px; margin: 0 auto; } .chart-container { background: rgba(30, 40, 60, 0.7); border-radius: 15px; padding: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.1); transition: transform 0.3s ease, box-shadow 0.3s ease; } .chart-container:hover { transform: translateY(-10px); box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4); background: rgba(40, 50, 80, 0.8); } .chart-title { font-size: 1.4rem; margin-bottom: 20px; text-align: center; color: #3498db; font-weight: bold; padding-bottom: 10px; border-bottom: 2px solid rgba(52, 152, 219, 0.3); } .chart { height: 400px; width: 100%; } .full-width { grid-column: span 2; } .stats { display: flex; justify-content: space-around; text-align: center; margin-top: 30px; } .stat-box { background: rgba(44, 62, 80, 0.7); padding: 20px; border-radius: 12px; min-width: 200px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); transition: all 0.3s ease; } .stat-box:hover { transform: scale(1.05); background: rgba(52, 73, 94, 0.8); } .stat-value { font-size: 2.5rem; font-weight: bold; color: #00f2fe; margin: 10px 0; } .stat-label { font-size: 1.1rem; color: #bdc3c7; } .footer { text-align: center; margin-top: 40px; padding: 20px; color: #7f8c8d; font-size: 1rem; border-top: 1px solid rgba(255, 255, 255, 0.1); } @media (max-width: 1200px) { .dashboard { grid-template-columns: 1fr; } .full-width { grid-column: span 1; } } </style> </head> <body> <div class="header"> <h1>餐厅小费数据分析大屏</h1> <p>消费行为与顾客特征可视化分析</p> </div> <div class="stats"> <div class="stat-box"> <div class="stat-label">总订单数</div> <div class="stat-value">244</div> </div> <div class="stat-box"> <div class="stat-label">平均小费比例</div> <div class="stat-value">16.1%</div> </div> <div class="stat-box"> <div class="stat-label">最高小费金额</div> <div class="stat-value">$10.00</div> </div> <div class="stat-box"> <div class="stat-label">晚餐平均小费</div> <div class="stat-value">$3.10</div> </div> </div> <div class="dashboard"> <div class="chart-container"> <div class="chart-title">消费金额与小费关系</div> <div id="scatterChart" class="chart"></div> </div> <div class="chart-container"> <div class="chart-title">不同性别小费比例</div> <div id="genderChart" class="chart"></div> </div> <div class="chart-container"> <div class="chart-title">不同时段用餐人数分布</div> <div id="sizeChart" class="chart"></div> </div> <div class="chart-container"> <div class="chart-title">小费比例分布分析</div> <div id="boxChart" class="chart"></div> </div> <div class="chart-container full-width"> <div class="chart-title">不同日期小费金额分布</div> <div id="dayChart" class="chart"></div> </div> <div class="chart-container full-width"> <div class="chart-title">吸烟与非吸烟顾客小费对比</div> <div id="smokerChart" class="chart"></div> </div> </div> <div class="footer"> 智能终端开发与数据可视化课程设计 | 徐丹晨 2022326603023 </div> <script> // 初始化所有图表 const scatterChart = echarts.init(document.getElementById('scatterChart')); const genderChart = echarts.init(document.getElementById('genderChart')); const sizeChart = echarts.init(document.getElementById('sizeChart')); const boxChart = echarts.init(document.getElementById('boxChart')); const dayChart = echarts.init(document.getElementById('dayChart')); const smokerChart = echarts.init(document.getElementById('smokerChart')); // 散点图配置 const scatterOption = { tooltip: { trigger: 'axis', formatter: '消费: ${c0}<br/>小费: ${c1}' }, xAxis: { name: '总消费金额(美元)', type: 'value', axisLine: { lineStyle: { color: '#7f8c8d' } } }, yAxis: { name: '小费金额(美元)', type: 'value', axisLine: { lineStyle: { color: '#7f8c8d' } } }, series: [{ symbolSize: 10, data: [ [16.99, 1.01], [10.34, 1.66], [21.01, 3.50], [23.68, 3.31], [24.59, 3.61], [25.29, 4.71], [8.77, 2.00], [26.88, 3.12], [15.04, 1.96], [14.78, 3.23], [10.27, 1.71], [35.26, 5.00], [15.42, 1.57], [18.43, 3.00], [14.83, 3.02], [21.58, 3.92] ], type: 'scatter', itemStyle: { color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [ { offset: 0, color: 'rgba(0, 210, 255, 0.8)' }, { offset: 1, color: 'rgba(0, 150, 255, 0.5)' } ]) } }], grid: { left: '10%', right: '10%', bottom: '15%', top: '15%' } }; // 性别小费比例配置 const genderOption = { tooltip: { trigger: 'axis', formatter: '{b}: {c}%' }, xAxis: { type: 'category', data: ['男性', '女性'], axisLine: { lineStyle: { color: '#7f8c8d' } } }, yAxis: { type: 'value', name: '小费比例(%)', axisLine: { lineStyle: { color: '#7f8c8d' } } }, series: [{ data: [16.7, 15.3], type: 'bar', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#00c6ff' }, { offset: 1, color: '#0072ff' } ]) }, label: { show: true, position: 'top', formatter: '{c}%' } }], grid: { left: '15%', right: '10%', bottom: '15%', top: '15%' } }; // 用餐人数分布配置 const sizeOption = { tooltip: { trigger: 'axis' }, legend: { data: ['午餐', '晚餐'], textStyle: { color: '#ecf0f1' } }, xAxis: { type: 'category', data: ['1人', '2人', '3人', '4人', '5人', '6人'], axisLine: { lineStyle: { color: '#7f8c8d' } } }, yAxis: { type: 'value', name: '订单数量', axisLine: { lineStyle: { color: '#7f8c8d' } } }, series: [ { name: '午餐', type: 'bar', stack: 'total', data: [3, 30, 12, 6, 0, 1], itemStyle: { color: '#3498db' } }, { name: '晚餐', type: 'bar', stack: 'total', data: [1, 112, 35, 30, 5, 4], itemStyle: { color: '#9b59b6' } } ], grid: { left: '10%', right: '10%', bottom: '15%', top: '20%' } }; // 小费比例分布配置 const boxOption = { tooltip: { trigger: 'item', formatter: '{a}: {b}' }, legend: { data: ['周四', '周五', '周六', '周日'], textStyle: { color: '#ecf0f1' } }, xAxis: { type: 'category', data: ['最小值', 'Q1', '中位数', 'Q3', '最大值'], axisLine: { lineStyle: { color: '#7f8c8d' } } }, yAxis: { type: 'value', name: '小费比例(%)', axisLine: { lineStyle: { color: '#7f8c8d' } } }, series: [ { name: '周四', type: 'boxplot', data: [[10.2, 13.5, 15.8, 17.2, 20.5]], itemStyle: { color: '#1abc9c' } }, { name: '周五', type: 'boxplot', data: [[11.0, 14.2, 16.1, 18.5, 22.0]], itemStyle: { color: '#3498db' } }, { name: '周六', type: 'boxplot', data: [[8.5, 15.0, 16.5, 18.0, 25.0]], itemStyle: { color: '#9b59b6' } }, { name: '周日', type: 'boxplot', data: [[12.0, 15.5, 17.0, 19.5, 22.5]], itemStyle: { color: '#e74c3c' } } ], grid: { left: '10%', right: '10%', bottom: '15%', top: '20%' } }; // 不同日期小费金额配置 const dayOption = { tooltip: { trigger: 'axis' }, legend: { data: ['周四', '周五', '周六', '周日'], textStyle: { color: '#ecf0f1' } }, radar: { indicator: [ { name: '平均小费', max: 5 }, { name: '小费比例', max: 25 }, { name: '消费金额', max: 40 }, { name: '订单数量', max: 90 } ] }, series: [{ type: 'radar', data: [ { value: [2.73, 16.2, 17.68, 40], name: '周四', itemStyle: { color: '#1abc9c' } }, { value: [2.99, 15.6, 19.29, 19], name: '周五', itemStyle: { color: '#3498db' } }, { value: [3.10, 15.3, 20.44, 87], name: '周六', itemStyle: { color: '#9b59b6' } }, { value: [3.25, 16.8, 21.41, 76], name: '周日', itemStyle: { color: '#e74c3c' } } ] }] }; // 吸烟与非吸烟顾客配置 const smokerOption = { tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' }, legend: { orient: 'vertical', right: 10, top: 'center', data: ['吸烟顾客', '非吸烟顾客'], textStyle: { color: '#ecf0f1' } }, series: [{ name: '顾客类型', type: 'pie', radius: ['40%', '70%'], center: ['40%', '50%'], itemStyle: { borderRadius: 10, borderColor: 'rgba(30, 40, 60, 1)', borderWidth: 2 }, label: { show: false }, emphasis: { label: { show: true, fontSize: '18', fontWeight: 'bold' } }, labelLine: { show: false }, data: [ { value: 93, name: '吸烟顾客', itemStyle: { color: '#e74c3c' } }, { value: 151, name: '非吸烟顾客', itemStyle: { color: '#2ecc71' } } ] }] }; // 应用配置 scatterChart.setOption(scatterOption); genderChart.setOption(genderOption); sizeChart.setOption(sizeOption); boxChart.setOption(boxOption); dayChart.setOption(dayOption); smokerChart.setOption(smokerOption); // 响应窗口大小变化 window.addEventListener('resize', function() { scatterChart.resize(); genderChart.resize(); sizeChart.resize(); boxChart.resize(); dayChart.resize(); smokerChart.resize(); }); </script> </body> </html> """ # 保存网页文件 with open('小费数据分析大屏.html', 'w', encoding='utf-8') as f: f.write(html_content) print("报告生成完成!") print("1. Word报告文件: 2022326603023-徐丹晨-数据可视化大作业报告.docx") print("2. 网页大屏文件: 小费数据分析大屏.html") print("3. 可视化图表: scatter_plot.png, gender_plot.png, box_plot.png")C:\python\Python311\python.exe "E:\final report\main.py" Traceback (most recent call last): File "E:\final report\main.py", line 4, in <module> import docx File "C:\python\Python311\Lib\site-packages\docx.py", line 30, in <module> from exceptions import PendingDeprecationWarning ModuleNotFoundError: No module named 'exceptions'
06-17
<template> <div id="cesiumContainer"></div> <div ref="miniMapContainer" class="mini-map" @click="handleMiniMapClick"> <div class="location-indicator" :style="indicatorStyle"></div> <!-- 四个角折角 --> <div class="corner corner-top-left"></div> <div class="corner corner-top-right"></div> <div class="corner corner-bottom-left"></div> <div class="corner corner-bottom-right"></div> <!-- <div class="camera-icon">📷</div> --> <div class="focus-effect"></div> <div class="pulse"></div> </div> <Search @location-selected="handleLocationSelected" /> <LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" /> </template> <style> /* @import "/temp/css/divGraphic.css"; */ </style> <script setup lang="ts"> import { computed, onUnmounted, onMounted, reactive } from "vue"; import LocationBar from "./location-bar.vue"; import Search from "./search.vue"; import initMap from "./init"; import { ref } from "vue"; import { throttle } from 'lodash'; import { loadRipplePoints, createMultipleRippleCircles } from './circle.js'; import { $prototype } from "../../main.ts"; const miniMapContainer = ref<HTMLElement>(); let viewIndicator: Rectangle; // 视图指示器样式 const currentPosition = reactive({ longitude: 113.361538, latitude: 27.339318, }); // 株洲市范围常量 const ZHUZHOU_EXTENT = { west: 112.5, // 株洲市西边界 east: 114.5, // 株洲市东边界 south: 26.0, // 株洲市南边界 north: 28.0 // 株洲市北边界 }; const rippleEntities = ref<any[]>([]); // 存储波纹圆实体 const heightThreshold = 80000; // 高度阈值(米),高于此值隐藏波纹圆 // 修复1:重新定义 indicatorStyle 为 ref const indicatorStyle = ref({ left: '50%', top: '50%', display: "block" }); const updateRippleVisibility = throttle(() => { if (!$prototype.$map || rippleEntities.value.length === 0) return; // 修复1:将 shouldShow 提取到块级作用域外部 let shouldShow = false; // 获取相机高度 const cartographic = $prototype.$map.camera.positionCartographic; // 修复2:添加空值检查 if (cartographic) { const cameraHeight = cartographic.height; shouldShow = cameraHeight > heightThreshold; } // 修复3:确保 shouldShow 已定义 rippleEntities.value.forEach(entity => { entity.show = shouldShow; }); }, 200); // 更新指示器位置 const updateIndicatorPosition = () => { if (!$prototype.$map) return; const camera = $prototype.$map.camera; const rect = camera.computeViewRectangle(); if (!rect) return; // 计算在株洲市范围内的相对位置 const center = Cesium.Rectangle.center(rect); const lon = Cesium.Math.toDegrees(center.longitude); const lat = Cesium.Math.toDegrees(center.latitude); // 确保位置在株洲市范围内 const constrainedLon = Math.max(ZHUZHOU_EXTENT.west, Math.min(ZHUZHOU_EXTENT.east, lon)); const constrainedLat = Math.max(ZHUZHOU_EXTENT.south, Math.min(ZHUZHOU_EXTENT.north, lat)); const lonPercent = ((constrainedLon - ZHUZHOU_EXTENT.west) / (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) * 100; const latPercent = 100 - ((constrainedLat - ZHUZHOU_EXTENT.south) / (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) * 100; // 更新CSS指示器 indicatorStyle.value = { left: `${lonPercent}%`, top: `${latPercent}%`, display: "block" }; }; // 更新鹰眼地图 - 只更新指示器位置 const updateOverview = () => { if (!$prototype.$map || !overviewViewer.value) return; // 获取主地图的当前视图中心 const rectangle = $prototype.$map.camera.computeViewRectangle(); if (!rectangle) return; // 更新指示器位置 updateIndicatorPosition(); }; const overviewViewer = ref(null); // 初始化鹰眼地图 - 使用全球底图+株洲市叠加层 const initMiniMap = () => { Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM"; if (!miniMapContainer.value) return; // 创建全球底图提供器 const worldProvider = new Cesium.UrlTemplateImageryProvider({ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", minimumLevel: 0, maximumLevel: 19, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); // 创建株洲市专用影像提供器 const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({ url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", minimumLevel: 1, maximumLevel: 17, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); // 鹰眼地图初始化 - 使用全球底图 overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, { sceneMode: Cesium.SceneMode.SCENE2D, baseLayerPicker: false, homeButton: false, timeline: false, navigationHelpButton: false, animation: false, scene3DOnly: true, selectionIndicator: false, infoBox: false, imageryProvider: worldProvider, // 使用全球底图 terrainProvider: undefined, mapProjection: new Cesium.WebMercatorProjection(), skyBox: false, skyAtmosphere: false }); // 添加株洲市影像作为叠加层 const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider(zhuzhouProvider); // 设置固定视野为株洲市范围 overviewViewer.value.camera.setView({ destination: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ) }); // 隐藏控件 const toolbar = overviewViewer.value.container.getElementsByClassName("cesium-viewer-toolbar")[0]; if (toolbar) toolbar.style.display = "none"; // 隐藏版权信息 overviewViewer.value.cesiumWidget.creditContainer.style.display = "none"; // 初始化视图指示器 initRectangle(); // 新增:添加白色边界到鹰眼地图 const addBoundaryToMiniMap = () => { overviewViewer.value.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.WHITE, fill: false, clampToGround: true, describe: null, }) ).then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { entity.polyline.fill = false; // 设置线宽和样式 entity.polyline.width = 2; entity.polyline.material = new Cesium.PolylineOutlineMaterialProperty({ color: Cesium.Color.WHITE, outlineWidth: 1, outlineColor: Cesium.Color.BLACK }); } } }).catch((error) => { console.error("加载鹰眼地图边界失败:", error); }); }; // 调用添加边界函数 addBoundaryToMiniMap(); }; // 初始化视图指示器 function initRectangle() { // 创建视图指示器(株洲市范围框) viewIndicator = overviewViewer.value.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), material: Cesium.Color.RED.withAlpha(0.3), outline: true, outlineColor: Cesium.Color.RED, outlineWidth: 2, }, }); } const loaded = ref(false); const useDmsFormat = ref(false); function addDemoGraphics() { const chinaBoundary = $prototype.$map.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.WHITE, fill: false, clampToGround: true, describe: null, }) ); chinaBoundary.then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { entity.polyline.fill = false; } } }); } function flyToDes() { const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0); $prototype.$map.camera.flyTo({ destination: new Cesium.Cartesian3( -2432812.6687511606, 5559483.804371395, 2832009.419525571 ), orientation: { heading: 6.283185307179421, pitch: -1.0472145569408116, roll: 6.2831853071795205, }, complete: function () {}, }); } // 监听主地图相机变化 const setupCameraListener = () => { $prototype.$map.camera.changed.addEventListener(updateOverview); }; // 鹰眼地图点击处理 const handleMiniMapClick = (event: MouseEvent) => { if (!miniMapContainer.value || !overviewViewer.value) return; const rect = miniMapContainer.value.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; // 计算在固定范围内的相对位置 const xPercent = (x / rect.width) * 100; const yPercent = (y / rect.height) * 100; // 转换为株洲市范围内的经纬度 const lon = ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west); const lat = ZHUZHOU_EXTENT.north - (yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south); // 主地图飞向点击位置 $prototype.$map.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000), }); }; function addImage() { var rightImageProvider = new Cesium.UrlTemplateImageryProvider({ name: "影像图", type: "xyz", layer: "vec_d", url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", minimumLevel: 1, maximumLevel: 17, crs: "EPSG:3857", }); $prototype.$map.imageryLayers.addImageryProvider(rightImageProvider); rightImageProvider.splitDirection = Cesium.SplitDirection.right; } onMounted(() => { initMap(); addImage(); loaded.value = true; addDemoGraphics(); flyToDes(); // 修复6:确保正确的初始化顺序 setTimeout(() => { initMiniMap(); setupCameraListener(); // 初始更新 updateOverview(); }, 1000); (async () => { try { const ripplePoints = await loadRipplePoints(); rippleEntities.value = createMultipleRippleCircles( $prototype.$map, ripplePoints ); $prototype.$map.camera.changed.addEventListener(updateRippleVisibility); updateRippleVisibility(); } catch (error) { console.error('加载波纹圆失败:', error); } })(); }); let currentMarker: any = null; const createMarker = (location: { lng: number; lat: number; name: string }) => { if (currentMarker) { $prototype.$map.entities.remove(currentMarker); } currentMarker = $prototype.$map.entities.add({ position: Cesium.Cartesian3.fromDegrees( location.lng, location.lat, 50 ), point: { pixelSize: 200, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2, heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, }, label: { text: location.name, font: "40px sans-serif", fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 1, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -20), heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), }, description: `<div style="padding: 10px;"> <h3 style="margin: 0 0 5px 0;">${location.name}</h3> <p>经度:${location.lng.toFixed(6)}</p> <p>纬度:${location.lat.toFixed(6)}</p> </div>`, }); return currentMarker; }; const handleLocationSelected = (location: { lng: number; lat: number; name: string; }) => { if (!$prototype.$map) return; const destination = Cesium.Cartesian3.fromDegrees( location.lng, location.lat, 200 ); $prototype.$map.camera.flyTo({ destination, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0, }, duration: 2, complete: () => { createMarker(location); }, }); }; onUnmounted(() => { if ($prototype.$map) { $prototype.$map.destroy(); $prototype.$map = null; } if ($prototype.$map) { $prototype.$map.camera.changed.removeEventListener(updateRippleVisibility); } console.log("组件销毁"); }); const emit = defineEmits(["onload", "onclick"]); const initMars3d = async (option: any) => { emit("onclick", true); emit("onload", $prototype.$map); }; </script> <style lang="less"> /**cesium 工具按钮栏*/ .cesium-viewer-toolbar { top: auto !important; bottom: 35px !important; left: 12px !important; right: auto !important; } .cesium-toolbar-button img { height: 100%; } .cesium-viewer-toolbar > .cesium-toolbar-button, .cesium-navigationHelpButton-wrapper, .cesium-viewer-geocoderContainer { margin-bottom: 5px; float: left; clear: both; text-align: center; } .cesium-button { background-color: rgba(23, 49, 71, 0.8); color: #e6e6e6; fill: #e6e6e6; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); line-height: 32px; } .cesium-button:hover { background: #3ea6ff; } /**cesium 底图切换面板*/ .cesium-baseLayerPicker-dropDown { bottom: 0; left: 40px; max-height: 700px; margin-bottom: 5px; background-color: rgba(23, 49, 71, 0.8); } /**cesium 帮助面板*/ .cesium-navigation-help { top: auto; bottom: 0; left: 40px; transform-origin: left bottom; background: none; background-color: rgba(23, 49, 71, 0.8); .cesium-navigation-help-instructions { background: none; } .cesium-navigation-button { background: none; } .cesium-navigation-button-selected, .cesium-navigation-button-unselected:hover { background: rgba(0, 138, 255, 0.2); } } /**cesium 二维三维切换*/ .cesium-sceneModePicker-wrapper { width: auto; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon { float: right; margin: 0 3px; } /**cesium POI查询输入框*/ .cesium-viewer-geocoderContainer .search-results { left: 0; right: 40px; width: auto; z-index: 9999; } .cesium-geocoder-searchButton { background-color: rgba(23, 49, 71, 0.8); } .cesium-viewer-geocoderContainer .cesium-geocoder-input { background-color: rgba(63, 72, 84, 0.7); } .cesium-viewer-geocoderContainer .cesium-geocoder-input:focus { background-color: rgba(63, 72, 84, 0.9); } .cesium-viewer-geocoderContainer .search-results { background-color: rgba(23, 49, 71, 0.8); } /**cesium info信息框*/ .cesium-infoBox { top: 50px; background-color: rgba(23, 49, 71, 0.8); } .cesium-infoBox-title { background-color: rgba(23, 49, 71, 0.8); } /**cesium 任务栏的FPS信息*/ .cesium-performanceDisplay-defaultContainer { top: auto; bottom: 35px; right: 50px; } .cesium-performanceDisplay-ms, .cesium-performanceDisplay-fps { color: #fff; } /**cesium tileset调试信息面板*/ .cesium-viewer-cesiumInspectorContainer { top: 10px; left: 10px; right: auto; } .cesium-cesiumInspector { background-color: rgba(23, 49, 71, 0.8); } /**覆盖mars3d内部控件的颜色等样式*/ .mars3d-compass .mars3d-compass-outer { fill: rgba(23, 49, 71, 0.8); } .mars3d-contextmenu-ul, .mars3d-sub-menu { background-color: rgba(23, 49, 71, 0.8); > li > a:hover, > li > a:focus, > li > .active { background-color: #3ea6ff; } > .active > a, > .active > a:hover, > .active > a:focus { background-color: #3ea6ff; } } /* Popup样式*/ .mars3d-popup-color { color: #ffffff; } .mars3d-popup-background { background: rgba(23, 49, 71, 0.8); } .mars3d-popup-content { margin: 15px; } .mars3d-template-content label { padding-right: 6px; } .mars3d-template-titile { border-bottom: 1px solid #3ea6ff; } .mars3d-template-titile a { font-size: 16px; } .mars3d-tooltip { background: rgba(23, 49, 71, 0.8); border: 1px solid rgba(23, 49, 71, 0.8); } .mars3d-popup-btn-custom { padding: 3px 10px; border: 1px solid #209ffd; background: #209ffd1c; } .mars-dialog .mars-dialog__content { height: 100%; width: 100%; overflow: auto; padding: 5px; } .image { border: solid 2px #fff; } .content { height: 90%; padding-top: 10px; overflow-x: auto; overflow-y: auto; } .content-text { padding: 0 10px; text-indent: 30px; font-size: 17px; } .details-video { width: 100%; height: 760px; background-color: #000; } :where(.css-lt97qq9).ant-space { display: inline-flex; } :where(.css-lt97qq9).ant-space-align-center { align-items: center; } :where(.css-lt97qq9).ant-image .ant-image-mask { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; background: rgba(0, 0, 0, 0.5); cursor: pointer; opacity: 0; transition: opacity 0.3s; } :where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 4px; } :where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] { box-sizing: border-box; } :where(.css-lt97qq9).ant-image .ant-image-img { width: 100%; height: auto; vertical-align: middle; } </style> <style scoped> .mini-map-container { position: relative; width: 100%; height: 100%; } .main-viewer { width: 100%; height: 100%; } .mini-map { position: absolute; right: 3vw; bottom: 6vh; width: 12vw; height: 17vh; border: 2px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 999; cursor: pointer; overflow: hidden; } .location-indicator { position: absolute; width: 14px; height: 14px; background: #ff3e3e; border: 2px solid white; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 15px rgba(255, 62, 62, 1); z-index: 100; } .view-rectangle { position: absolute; border: 2px solid #00f2fe; z-index: 99; pointer-events: none; box-shadow: 0 0 20px rgba(0, 242, 254, 0.7); } /* 相机聚焦样式 - 四个角折角 */ .corner { position: absolute; width: 25px; height: 25px; z-index: 100; pointer-events: none; } .corner::before, .corner::after { content: ""; position: absolute; background: #00f2fe; box-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .corner-top-left { top: -2px; left: -2px; } .corner-top-left::before { top: 0; left: 0; width: 15px; height: 3px; } .corner-top-left::after { top: 0; left: 0; width: 3px; height: 15px; } .corner-top-right { top: -2px; right: -2px; } .corner-top-right::before { top: 0; right: 0; width: 15px; height: 3px; } .corner-top-right::after { top: 0; right: 0; width: 3px; height: 15px; } .corner-bottom-left { bottom: -2px; left: -2px; } .corner-bottom-left::before { bottom: 0; left: 0; width: 15px; height: 3px; } .corner-bottom-left::after { bottom: 0; left: 0; width: 3px; height: 15px; } .corner-bottom-right { bottom: -2px; right: -2px; } .corner-bottom-right::before { bottom: 0; right: 0; width: 15px; height: 3px; } .corner-bottom-right::after { bottom: 0; right: 0; width: 3px; height: 15px; } .camera-icon { position: absolute; top: 10px; right: 10px; color: #00f2fe; font-size: 24px; z-index: 100; text-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .focus-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 98; border: 2px solid rgba(0, 242, 254, 0.2); border-radius: 5px; box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1); } .pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10px; height: 10px; border-radius: 50%; background: rgba(0, 242, 254, 0.7); box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7); animation: pulse 2s infinite; } ::v-deep.cesium-viewer-toolbar { display: none; } </style> 如上所示代码,还是没有显示白色边界
07-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值