stevenjin 2025-05-08 23:02 采纳率: 96.8%
浏览 36
已结题

请调试一下ucharts横屏时曲势图的显示

需求:
1.用unapp及ucharts生成曲势图
2.点击横屏按钮,实现类似bilibili的横屏效果。点击返回按钮,恢复横屏效果。不需要去手机设置开“自动旋转“
3.横屏后,要铺满整个屏幕
以下代码是用deepseek生成的,但真机横屏后,趋势图不能铺满全屏,还是保留了坚屏时的宽度。请帮忙调一下。


<template>
  <view class="container">
    <!-- 横屏模式 -->
    <view v-if="isLandscape" class="landscape-view">
      <qiun-data-charts 
        type="line"
        :chartData="chartData"
        :opts="landscapeOpts"
        :canvas2d="true"
        canvasId="landscapeChart"
        @legendTap="handleLegendTap"
      />
      <button class="exit-btn" @click="toggleOrientation">返回竖屏</button>
    </view>

    <!-- 竖屏模式 -->
    <view v-else class="portrait-view">
      <qiun-data-charts 
        type="line"
        :chartData="chartData"
        :opts="portraitOpts"
        :canvas2d="true"
        canvasId="portraitChart"
        @legendTap="handleLegendTap"
      />
      
      <view class="control-panel">
        <button class="landscape-btn" @click="toggleOrientation">横屏查看</button>
        
        <view class="filter-row">
          <picker class="time-picker" :value="timeIndex" :range="timeOptions" @change="handleTimeChange">
            <view class="picker-content">
              {{ timeOptions[timeIndex] }}
              <view class="arrow-down"></view>
            </view>
          </picker>
          <input 
            class="custom-input" 
            v-model="customHours" 
            type="number" 
            placeholder="自定义小时"
            @blur="handleCustomInput"
          />
          <button @click="loadData">查询</button>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
const STORAGE_KEY = 'chartLegendStatus';

export default {
  data() {
    return {
      isLandscape: false,
      timeIndex: 3,
      timeOptions: ['1小时', '4小时', '8小时', '24小时', '自定义'],
      customHours: '',
      chartData: {},
      colorPool: [
        "#1890FF","#FF6B6B","#6BCB77","#F0B400",
        "#9B59B6","#FF7F50","#1ABC9C","#E74C3C"
      ],
      portraitOpts: {
        fontSize: 11,
        dataLabel: false,
        animation: false,
        legend: {
          show: true,
          position: "bottom",
          clickable: true,
          lineHeight: 30,
          maxLine: 2,
          itemGap: 15
        },
        xAxis: {
          showLabel: false,
          disabled: true,
          axisLine: { show: false },
          splitLine: { show: false },
          labelStyle: {
            color: 'transparent',
            fontSize: 0
          },
          format: () => ''
        },
        yAxis: {
          gridType: 'dash',
          dashLength: 4
        },
        extra: {
          line: {
            type: 'curve',
            width: 1.5,
            activeType: 'none'
          }
        }
      },
      landscapeOpts: {
        fontSize: 14,
        dataLabel: false,
        animation: false,
        padding: [15, 60, 15, 15], // 调整padding适应横屏
        pixelRatio: 2,
        legend: {
          show: true,
          position: "right",
          clickable: true,
          lineHeight: 20,
          maxLine: 10,
          itemGap: 8,
          margin: 5,
          formatter: name => name.length > 6 ? name.substring(0, 6) + '...' : name // 缩短长图例
        },
        xAxis: {
          disabled: false,
          showLabel: true,
          axisLine: { show: true },
          splitLine: { show: true },
          labelCount: 8,
          labelStyle: {
            color: '#666',
            fontSize: 10,
            rotate: 45 // x轴标签旋转45度防止重叠
          }
        },
        yAxis: {
          gridType: 'dash',
          dashLength: 4,
          splitNumber: 5,
          axisLine: { show: true },
          labelStyle: {
            color: '#666',
            fontSize: 10
          }
        },
        extra: {
          line: {
            type: 'curve',
            width: 1.5,
            activeType: 'none',
            addLine: true
          }
        }
      }
    };
  },
  computed: {
    filterHours() {
      const preset = [1,4,8,24][this.timeIndex];
      return this.timeIndex === 4 ? Math.min(720, this.customHours || 0) : preset;
    }
  },
  mounted() {
    this.loadPersistedData();
    this.loadData();
    // #ifdef APP-PLUS
    plus.screen.lockOrientation('portrait-primary');
    // #endif
  },
  methods: {
    toggleOrientation() {
      this.isLandscape = !this.isLandscape;
      
      // #ifdef APP-PLUS
      setTimeout(() => {
        plus.screen.lockOrientation(
          this.isLandscape ? 'landscape-primary' : 'portrait-primary'
        );
      }, 100);
      // #endif
      
      this.$nextTick(() => {
        this.chartData = JSON.parse(JSON.stringify(this.chartData));
      });
    },
    
    loadPersistedData() {
      const savedStatus = uni.getStorageSync(STORAGE_KEY);
      if (savedStatus) {
        this.chartData.series = savedStatus;
      }
    },
    
    handleTimeChange(e) {
      this.timeIndex = e.detail.value;
      if (this.timeIndex !== 4) this.loadData();
    },
    
    handleCustomInput() {
      if (this.customHours > 0) {
        this.timeIndex = 4;
        this.loadData();
      }
    },
    
    async loadData() {
      const hours = Math.max(1, Math.min(720, this.filterHours));
      const mockData = await this.getMockData(hours);
      
      this.chartData = {
        categories: mockData.categories,
        series: mockData.values.map((dataArr, index) => ({
          name: `传感器${index + 1}`,
          data: dataArr,
          color: this.colorPool[index % 8],
          show: this.getSeriesVisibility(index)
        }))
      };
    },
    
    handleLegendTap(e) {
      if (!e?.detail?.index) return;
      
      const index = e.detail.index;
      const newSeries = this.chartData.series.map((item, i) => 
        i === index ? {...item, show: !item.show} : item
      );
      
      uni.setStorageSync(STORAGE_KEY, newSeries);
      this.chartData = {...this.chartData, series: newSeries};
    },
    
    getSeriesVisibility(index) {
      const saved = uni.getStorageSync(STORAGE_KEY);
      return (saved && saved[index]) ? saved[index].show : true;
    },
    
    generateTimeLabels(hours) {
      return Array.from({length: hours}, (_,i) => {
        const date = new Date(Date.now() - (hours - i - 1)*3600000);
        return this.formatTime(date);
      });
    },
    
    generateTrendData(length) {
      let current = 50;
      return Array.from({length}, () => {
        current += Math.random() * 4 - 2;
        return Math.min(Math.max(Math.round(current), 0), 100);
      });
    },
    
    formatTime(date) {
      const pad = n => n.toString().padStart(2,'0');
      return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
    },
    
    getMockData(hours) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve({
            categories: this.generateTimeLabels(hours),
            values: Array.from({length:8}, () => this.generateTrendData(hours))
          });
        }, 300);
      });
    }
  }
};
</script>

<style scoped>
.container {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow: hidden;
}

/* 竖屏样式 */
.portrait-view {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.portrait-view .qiun-data-charts {
  flex: 1;
  width: 100% !important;
}

.control-panel {
  padding: 15px;
  background: #f8f8f8;
  border-top: 1px solid #eee;
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 15px;
}

.time-picker {
  border: 1px solid #dcdfe6;
  border-radius: 8rpx;
  padding: 10px 15px;
  flex: 1;
}

.picker-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.arrow-down {
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 8px solid #999;
  margin-left: 8px;
}

.custom-input {
  flex: 1;
  border: 1px solid #dcdfe6;
  border-radius: 8rpx;
  padding: 10px;
}

button {
  background-color: #1890ff;
  color: white;
  border: none;
  border-radius: 5px;
  padding: 10px 15px;
  font-size: 14px;
}

.landscape-btn {
  background-color: #1abc9c;
  width: 100%;
}

/* 横屏样式 */
.landscape-view {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: white;
  z-index: 1000;
  display: flex;
  flex-direction: column;
}

.landscape-view .qiun-data-charts {
  flex: 1;
  width: 100% !important;
  height: 100% !important;
}

.exit-btn {
  position: absolute;
  bottom: 20px;
  right: 20px;
  height: 40px;
  min-width: 90px;
  padding: 0 15px;
  background-color: #ff4d4f;
  color: white;
  border-radius: 20px;
  z-index: 1001;
  box-shadow: 0 2px 10px rgba(0,0,0,0.15);
  font-size: 14px;
}

/* 强制图表尺寸 */
:deep(.qiun-data-charts),
:deep(.qiun-charts),
:deep(canvas) {
  width: 100% !important;
  height: 100% !important;
  display: block !important;
}
</style>

  • 写回答

4条回答 默认 最新

  • 阿里嘎多学长 2025-05-08 23:23
    关注

    阿里嘎多学长整理AIGC生成,因移动端显示问题导致当前答案未能完全显示,请使用PC端查看更加详细的解答过程

    问题解答

    你想要使用 UCharts 生成曲势图,并在横屏时实现类似 Bilibili 的横屏效果,点击返回按钮恢复竖屏效果。

    首先,你需要在 HTML 中添加一个 div 元素来容纳 UCharts:

    <div id="chart"></div>
    

    然后,在 JavaScript 中使用 UCharts 生成曲势图:

    const chart = new UCharts({
      container: 'chart',
      type: 'line',
      legend: 'none',
      data: [...], // 数据
      xLabel: '时间',
      yLabel: '值',
      width: '100%',
      height: '100%',
    });
    

    接下来,你需要添加一个按钮来实现横屏和竖屏切换:

    <button id="rotate-btn">横屏</button>
    

    然后,在 JavaScript 中添加事件监听器来实现横屏和竖屏切换:

    document.getElementById('rotate-btn').addEventListener('click', () => {
      const chartContainer = document.getElementById('chart');
      if (chartContainer.requestFullscreen()) {
        // 横屏
        chartContainer.style.width = '100%';
        chartContainer.style.height = '100vh';
        chartContainer.style.transform = 'rotate(90deg)';
      } else {
        // 竖屏
        chartContainer.style.width = '100vw';
        chartContainer.style.height = '100vh';
        chartContainer.style.transform = 'rotate(0deg)';
      }
    });
    

    最后,你需要添加一个返回按钮来恢复竖屏效果:

    <button id="back-btn">返回</button>
    

    然后,在 JavaScript 中添加事件监听器来实现返回按钮的点击事件:

    document.getElementById('back-btn').addEventListener('click', () => {
      const chartContainer = document.getElementById('chart');
      chartContainer.style.width = '100vw';
      chartContainer.style.height = '100vh';
      chartContainer.style.transform = 'rotate(0deg)';
    });
    

    这样,你就可以实现类似 Bilibili 的横屏效果了。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 5月17日
  • 已采纳回答 5月9日
  • 创建了问题 5月8日