情绪周期。打板必知必会~

冰点

混沌、酝酿、启动

情绪发酵

高峰高潮

退潮

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>市场情绪周期可视化</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
  <style>
 :root {
      --blue: #3b82f6;
      --green: #10b981;
      --yellow: #f59e0b;
      --red: #ef4444;
      --gray: #6b7280;
      --transition-duration: 0.4s;
    }
    
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
    }
    
    body {
      background-color: #f8fafc;
      color: #1e293b;
      line-height: 1.6;
      overflow-x: hidden;
    }
    
    .container {
      max-width: 1200px;
      margin: 0 auto;
      padding: 2rem;
    }
    
    header {
      text-align: center;
      margin-bottom: 3rem;
      position: relative;
    }
    
    h1 {
      font-size: 2.5rem;
      margin-bottom: 1rem;
      background: linear-gradient(135deg, var(--blue), var(--green), var(--yellow), var(--red), var(--gray));
      -webkit-background-clip: text;
      background-clip: text;
      color: transparent;
      animation: gradient 10s ease infinite;
      background-size: 400% 400%;
    }
    
    @keyframes gradient {
      0% { background-position: 0% 50%; }
      50% { background-position: 100% 50%; }
      100% { background-position: 0% 50%; }
    }
    
    .subtitle {
      font-size: 1.2rem;
      color: #64748b;
    }
    
    /* 修改后的顶部导航样式 - 透明无边框 */
    .phase-navigation {
      position: sticky;
      top: 0;
      background-color: transparent; /* 改为透明背景 */
      z-index: 100;
      padding: 1rem 0;
      margin-bottom: 2rem;
      /* 移除 box-shadow */
    }
    
    .phase-buttons {
      display: flex;
      justify-content: center;
      gap: 0.5rem;
      flex-wrap: wrap;
      background-color: rgba(255, 255, 255, 0.8); /* 给按钮容器添加半透明白色背景 */
      backdrop-filter: blur(5px); /* 添加毛玻璃效果 */
      padding: 0.5rem;
      border-radius: 20px;
      max-width: 95%;
      margin: 0 auto;
    }
    
    .phase-button {
      padding: 0.5rem 1rem;
      border-radius: 20px;
      border: none;
      font-weight: bold;
      cursor: pointer;
      transition: all 0.3s;
      white-space: nowrap;
      /* 移除按钮的阴影效果 */
      box-shadow: none !important;
    }
    
    .phase-button:hover {
      transform: translateY(-2px);
      /* 移除悬停阴影 */
    }
    
    .cycle-container {
      display: flex;
      flex-direction: column;
      gap: 2rem;
      margin-top: 2rem;
    }
    
    /* Cycle Visualization */
    .cycle-visualization {
      height: 500px;
      position: relative;
      margin-bottom: 3rem;
    }
    
    .cycle-path {
      width: 90%;
      height: 300px;
      margin: 0 auto;
      position: relative;
    }
    
    .cycle-line {
      position: absolute;
      height: 4px;
      width: 0;
      background: linear-gradient(90deg, 
        var(--blue) 0%, var(--blue) 20%,
        var(--green) 20%, var(--green) 40%,
        var(--yellow) 40%, var(--yellow) 60%,
        var(--red) 60%, var(--red) 80%,
        var(--gray) 80%, var(--gray) 100%
      );
      top: 50%;
      left: 0;
      transform: translateY(-50%);
      animation: drawLine 1.5s ease-out forwards;
      z-index: 1;
    }
    
    @keyframes drawLine {
      to { width: 100%; }
    }
    
    .nodes-container {
      position: absolute;
      width: 90%;
      height: 300px;
      top: 0;
      left: 5%;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    
    .node {
      width: 80px;
      height: 80px;
      border-radius: 50%;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      transition: all var(--transition-duration);
      opacity: 0;
      transform: scale(0.8);
      position: relative;
      z-index: 2;
      color: #fff;
      box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
    }
    
    .node:nth-child(1) { animation: fadeInNode 0.5s 0.3s forwards; }
    .node:nth-child(2) { animation: fadeInNode 0.5s 0.6s forwards; }
    .node:nth-child(3) { animation: fadeInNode 0.5s 0.9s forwards; }
    .node:nth-child(4) { animation: fadeInNode 0.5s 1.2s forwards; }
    .node:nth-child(5) { animation: fadeInNode 0.5s 1.5s forwards; }
    
    @keyframes fadeInNode {
      to {
        opacity: 1;
        transform: scale(1);
      }
    }
    
    .node:hover {
      transform: scale(1.1);
      box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
    }
    
    .node.active {
      transform: scale(1.1);
      box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
    }
    
    .node-icon {
      font-size: 1.5rem;
      margin-bottom: 0.3rem;
    }
    
    .node-name {
      font-size: 0.7rem;
      text-align: center;
      line-height: 1.2;
      padding: 0 5px;
    }
    
    .node-pulse {
      position: absolute;
      border-radius: 50%;
      animation: pulse 2s infinite;
      z-index: -1;
    }
    
    @keyframes pulse {
      0% {
        width: 100%;
        height: 100%;
        opacity: 0.8;
      }
      100% {
        width: 140%;
        height: 140%;
        opacity: 0;
      }
    }
    
    /* Detail Card */
    .detail-card {
      background-color: white;
      border-radius: 16px;
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
      padding: 2rem;
      margin-top: 2rem;
      transition: all var(--transition-duration);
      opacity: 0;
      transform: translateY(20px);
    }
    
    .detail-card.show {
      opacity: 1;
      transform: translateY(0);
    }
    
    .detail-header {
      display: flex;
      align-items: center;
      margin-bottom: 1.5rem;
      padding-bottom: 1rem;
      border-bottom: 1px solid #e2e8f0;
    }
    
    .detail-icon-container {
      width: 60px;
      height: 60px;
      border-radius: 12px;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-right: 1.5rem;
      flex-shrink: 0;
    }
    
    .detail-icon {
      font-size: 2rem;
      color: white;
    }
    
    .detail-title {
      flex-grow: 1;
    }
    
    .detail-name {
      font-size: 1.8rem;
      font-weight: 700;
      margin-bottom: 0.3rem;
    }
    
    .detail-alias {
      font-size: 1rem;
      color: #64748b;
    }
    
    .detail-description {
      margin-bottom: 2rem;
      line-height: 1.8;
    }
    
    .detail-content {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 2rem;
    }
    
    @media (max-width: 768px) {
      .detail-content {
        grid-template-columns: 1fr;
      }
    }
    
    .detail-section {
      margin-bottom: 1.5rem;
    }
    
    .detail-section-title {
      font-size: 1.2rem;
      font-weight: 600;
      margin-bottom: 1rem;
      display: flex;
      align-items: center;
    }
    
    .detail-section-title i {
      margin-right: 0.5rem;
    }
    
    .detail-list {
      list-style-type: none;
    }
    
    .detail-list li {
      padding: 0.5rem 0;
      display: flex;
      align-items: flex-start;
    }
    
    .detail-list li::before {
      content: "•";
      margin-right: 0.5rem;
      font-weight: bold;
    }
    
    /* Chart */
    .chart-container {
      height: 300px;
      margin: 2rem 0;
      position: relative;
    }
    
    .chart {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
    
    .chart-line {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 100%;
      fill: none;
      stroke-width: 3;
      stroke-linecap: round;
      stroke-linejoin: round;
      stroke-dasharray: 1000;
      stroke-dashoffset: 1000;
      animation: drawChart 2s ease-out forwards;
    }
    
    @keyframes drawChart {
      to { stroke-dashoffset: 0; }
    }
    
    .chart-area {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 100%;
      fill-opacity: 0.1;
    }
    
    .chart-labels {
      position: absolute;
      bottom: -30px;
      left: 0;
      width: 100%;
      display: flex;
      justify-content: space-between;
      padding: 0 10px;
    }
    
    .chart-label {
      font-size: 0.8rem;
      color: #64748b;
      text-align: center;
      width: 20%;
    }
    
    .chart-points {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    
    .chart-point {
      position: absolute;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      z-index: 2;
      cursor: pointer;
      transition: all 0.3s;
    }
    
    .chart-point.active {
      width: 14px;
      height: 14px;
      box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.5);
    }
    
    .chart-tooltip {
      position: absolute;
      background-color: white;
      border-radius: 8px;
      padding: 0.5rem 1rem;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
      font-size: 0.9rem;
      pointer-events: none;
      opacity: 0;
      transform: translateY(10px);
      transition: all 0.3s;
      z-index: 10;
    }
    
    .chart-tooltip.show {
      opacity: 1;
      transform: translateY(0);
    }
    
    /* Footer */
    footer {
      text-align: center;
      margin-top: 4rem;
      padding-top: 2rem;
      border-top: 1px solid #e2e8f0;
      color: #64748b;
      font-size: 0.9rem;
    }
    
    /* Animations */
    @keyframes float {
      0% { transform: translateY(0); }
      50% { transform: translateY(-10px); }
      100% { transform: translateY(0); }
    }
    
    .float {
      animation: float 4s ease-in-out infinite;
    }
    
    /* Responsive */
    @media (max-width: 768px) {
      .nodes-container {
        flex-direction: column;
        height: auto;
        gap: 1.5rem;
      }
      
      .cycle-line {
        height: 100%;
        width: 4px;
        top: 0;
        left: 50%;
        transform: translateX(-50%);
        background: linear-gradient(180deg, 
          var(--blue) 0%, var(--blue) 20%,
          var(--green) 20%, var(--green) 40%,
          var(--yellow) 40%, var(--yellow) 60%,
          var(--red) 60%, var(--red) 80%,
          var(--gray) 80%, var(--gray) 100%
        );
      }
      
      @keyframes drawLine {
        to { height: 100%; }
      }
      
      .cycle-visualization {
        height: 700px;
      }
      
      .cycle-path {
        height: 600px;
      }
      
      .detail-header {
        flex-direction: column;
        text-align: center;
      }
      
      .detail-icon-container {
        margin-right: 0;
        margin-bottom: 1rem;
      }
      
      .phase-buttons {
        gap: 0.3rem;
      }
      
      .phase-button {
        padding: 0.3rem 0.6rem;
        font-size: 0.8rem;
      }
    }
  </style>
</head>
<body>
  <div class="container">
    <header>
      <h1>市场情绪周期</h1>
      <p class="subtitle">了解市场情绪的变化,把握交易时机</p>
    </header>
    
    <!-- 新增的顶部导航 -->
    <div class="phase-navigation">
      <div class="phase-buttons">
        <button class="phase-button" style="background-color: #3b82f6; color: white;" data-id="1">情绪冰点期</button>
        <button class="phase-button" style="background-color: #10b981; color: white;" data-id="2">情绪混沌、酝酿转启动期</button>
        <button class="phase-button" style="background-color: #f59e0b; color: white;" data-id="3">情绪发酵期</button>
        <button class="phase-button" style="background-color: #ef4444; color: white;" data-id="4">情绪高峰、亢奋期</button>
        <button class="phase-button" style="background-color: #6b7280; color: white;" data-id="5">情绪退潮期</button>
      </div>
    </div>
    
    <div class="cycle-container">
      <div class="cycle-visualization">
        <div class="cycle-path">
          <div class="cycle-line"></div>
          <div class="nodes-container">
            <!-- Nodes will be generated by JavaScript -->
          </div>
        </div>
      </div>
      
      <div class="chart-container">
        <div class="chart">
          <svg width="100%" height="100%" viewBox="0 0 1000 300" preserveAspectRatio="none">
            <defs>
              <linearGradient id="chartGradient" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#3b82f6" />
                <stop offset="20%" stop-color="#3b82f6" />
                <stop offset="20%" stop-color="#10b981" />
                <stop offset="40%" stop-color="#10b981" />
                <stop offset="40%" stop-color="#f59e0b" />
                <stop offset="60%" stop-color="#f59e0b" />
                <stop offset="60%" stop-color="#ef4444" />
                <stop offset="80%" stop-color="#ef4444" />
                <stop offset="80%" stop-color="#6b7280" />
                <stop offset="100%" stop-color="#6b7280" />
              </linearGradient>
            </defs>
            <path class="chart-line" stroke="url(#chartGradient)" d="M0,250 C50,250 100,200 150,180 C200,160 250,150 300,120 C350,90 400,70 500,50 C600,30 650,40 700,70 C750,100 800,150 850,200 C900,250 950,250 1000,250" />
            <path class="chart-area" fill="url(#chartGradient)" d="M0,250 C50,250 100,200 150,180 C200,160 250,150 300,120 C350,90 400,70 500,50 C600,30 650,40 700,70 C750,100 800,150 850,200 C900,250 950,250 1000,250 L1000,300 L0,300 Z" />
          </svg>
          <div class="chart-points">
            <!-- Points will be generated by JavaScript -->
          </div>
          <div class="chart-labels">
            <div class="chart-label">情绪冰点期</div>
            <div class="chart-label">混沌转启动期</div>
            <div class="chart-label">情绪发酵期</div>
            <div class="chart-label">情绪高峰期</div>
            <div class="chart-label">情绪退潮期</div>
          </div>
          <div class="chart-tooltip"></div>
        </div>
      </div>
      
      <div class="detail-card">
        <!-- Details will be filled by JavaScript -->
      </div>
    </div>
    
    <footer>
      <p>© 2025 市场情绪周期分析 - 把握市场脉搏,顺势而为</p>
    </footer>
  </div>
  
  <script>
    // Data
    const emotionCycleData = [
      {
        id: 1,
        name: "情绪冰点期",
        alias: "情绪低落期",
        description: "在一轮明显的亏钱效应代表的退潮期之后,市场情绪会非常低迷,投资者对市场前景感到极度悲观,多空双方都没有了动力,情绪像结了冰一样,没有一点生机,偶有尝试也会失败。",
        features: [
          "总龙头连续杀跌20-30%",
          "涨停数量连续下降,甚至跌停家数超过了涨停家数",
          "连板高度一直受压制,最高板下降至4板、3板甚至2板以下",
          "龙头杀跌,补涨股也杀跌",
          "一些看起来很好的板,次日根本无溢价",
          "短线情绪极度冰点,市场没有主流热点",
          "没有持续的赚钱效应,大部分热点都是一日游",
          "可操作性极差,容易吃面"
        ],
        strategies: [
          "冷静、自信、坚定地观察、等待",
          "等待混沌期头铁的前浪们的厮杀博弈结束",
          "等待冰点、混沌期结束后,明显的启动期到来的信号",
          "多看少动,不当炮灰",
          "禁止做高位板接力",
          "直到情绪二冰过后,看到明显的多方信号"
        ],
        color: "#3b82f6", // 蓝色
        icon: "thermometer-snow"
      },
      {
        id: 2,
        name: "情绪混沌、酝酿转启动期",
        alias: "情绪混沌酝酿转上升阶段",
        description: "在情绪退潮期一轮极致的亏钱效应导致的市场情绪极致的悲观、冷却之后,随着做多情绪的慢慢积累,情绪逐渐转暖,从冰点转向混沌、酝酿做多转向启动,上一轮周期结束,新周期的种子开始萌发,对于市场的看法开始从一致的悲观到出现分歧,转入混沌、摸索、试探、酝酿启动阶段,市场情绪开始升温,投资者对于市场的前景逐渐开始乐观。",
        features: [
          "市场不再动辄出现天地大面",
          "昨日涨停开盘无溢价直接闷杀或昨日涨停今天跌停的现象几乎绝迹",
          "炸板股明显减少",
          "开始出现代表情绪好转的大长腿",
          "某只个股开始打破3板、4板的空间压制,逆势创出近期新高",
          "板块内个股走势都明显强于大盘,具有较强的板块效应",
          "题材质量容量颇有想象空间"
        ],
        strategies: [
          "可以考虑适当加仓",
          "关注一些具有潜力的板块和个股",
          "初期,应该以做首板为主",
          "中后期,可以小仓试错那些低位新起的、身位最高、竞价最强、有板块效应、有助攻的潜在龙头"
        ],
        color: "#10b981", // 绿色
        icon: "seedling"
      },
      {
        id: 3,
        name: "情绪发酵期",
        alias: "情绪扩散期",
        description: "出现在情绪启动期之后,是行情确认,分歧转一致的过程。这个阶段市场情绪进一步升温,投资者对市场的信心逐渐增强。",
        features: [
          "随着空间的进一步开拓,板块赚钱效应出现",
          "连板股数量开始增多"
        ],
        strategies: [
          "在情绪周期里面,最容易出妖股,最容易上车、赚钱的时期就是情绪发酵期和高潮期",
          "这个阶段是龙头选手大展身手、上仓位的最关键阶段",
          "在发酵期的初期,着重的是题材(属性)方向,要聚焦市场最主流热点的龙头",
          "做不到一字龙可在启动期第一时间上同身位的换手票,其次再考虑龙二",
          "在发酵期的中后期,可以考虑去做主流板块低位起涨的首板或2板票的补涨"
        ],
        color: "#f59e0b", // 黄色
        icon: "chart-line-up"
      },
      {
        id: 4,
        name: "情绪高峰、亢奋期",
        alias: "情绪主升、高潮阶段",
        description: "这个阶段投资者情绪非常高涨,信心爆棚,高潮达到亢奋,情绪到达顶峰,行情面临转折,风险即将来临。",
        features: [
          "主涨属性明确,总龙头确认",
          "龙头股一路上涨,核心股连续缩量加速",
          "龙头带跟风,跟风助龙头",
          "板块出现批量涨停潮,涨停晋级率非常高",
          "甚至大盘中军都能连续涨停",
          "涨停数量连续上升,甚至没有了跌停",
          "连板股空间高度不断打开,能达到6、7板以上",
          "主线热点票霸屏",
          "昨日成交金额前列皆是大涨"
        ],
        strategies: [
          "情绪主升期,着重的是情绪,胆大重仓就是效益",
          "竞价就抢龙头、抢一字涨停板,竞价不抢,你可能就买不到",
          "做焦点中的焦点、做最闪亮的市场总龙头",
          "如果总龙头大单一字不给机会,再考虑择强做低位1板、2板的卡位晋级或科创板的强势票",
          "先上车套利,待龙头分歧再换车总龙头",
          "总龙头涨到高位或者特停后,就围绕总龙头的主涨属性选择那些属性最正、股性最好的个股做补涨",
          "此时一定要不惜血本、不惜代价抢先手买在启动点",
          "此阶段,分歧加仓总龙头,重仓持股待涨就是最佳策略"
        ],
        color: "#ef4444", // 红色
        icon: "rocket"
      },
      {
        id: 5,
        name: "情绪退潮期",
        alias: "情绪降温阶段",
        description: "这个阶段市场的亏钱效应逐渐加大,赚钱效应逐渐消失,情绪从一致的做多到开始出现分化降温,投资者对市场的信心逐渐减弱,直至崩溃。",
        features: [
          "退潮期的开始,出现在一段上升周期的末端",
          "龙头还在走加速,低位也在补涨助攻,中位股却率先跳水",
          "情绪出现强列的分歧、分化",
          "总龙头断板即A杀,龙头股、跟风股纷纷开始补跌",
          "高位连板股接连出现亏钱效应,连扳高度逐步下降",
          "炸板大面票、昨日涨停今天跌停、闷杀的票批量出现",
          "尤其高位炸板股增多,极端的出现天地板大面",
          "低位股快速轮动,持续性极差"
        ],
        strategies: [
          "除了龙头的抱团走穿越,坚决减仓或者清仓",
          "特别是中位的跟风股,以避免市场继续下跌的风险",
          "情绪退潮期,着重的是筹码结构",
          "如果叠加指数下跌周期,亏钱效应扩散很快,资金争相离场,市场没有接力情绪",
          "各种模式都会失效,最佳策略就是空仓等待",
          "等待情绪退潮期的结束直至情绪冰点期的到来"
        ],
        color: "#6b7280", // 灰色
        icon: "wave-sine"
      }
    ];

    // Initialize
    document.addEventListener('DOMContentLoaded', function() {
      renderNodes();
      renderChartPoints();
      showDetail(1); // Show first phase by default
      
      // 添加顶部导航按钮点击事件
      const phaseButtons = document.querySelectorAll('.phase-button');
      phaseButtons.forEach(button => {
        button.addEventListener('click', function() {
          const id = parseInt(this.dataset.id);
          showDetail(id);
        });
      });
      
      // Initialize additional elements
      setTimeout(() => {
        createMarketVisualizer();
        createCycleSwitcher();
        createMarketDataTable();
      }, 1500);
    });
    
    // Render cycle nodes
    function renderNodes() {
      const nodesContainer = document.querySelector('.nodes-container');
      emotionCycleData.forEach((cycle, index) => {
        const node = document.createElement('div');
        node.className = 'node';
        node.style.backgroundColor = cycle.color;
        node.dataset.id = cycle.id;
        
        // Add pulse effect to the first node
        if (index === 0) {
          const pulse = document.createElement('div');
          pulse.className = 'node-pulse';
          pulse.style.backgroundColor = cycle.color;
          node.appendChild(pulse);
          node.classList.add('active');
        }
        
        const iconClass = getIconClass(cycle.icon);
        
        node.innerHTML = `
          <i class="node-icon ${iconClass}"></i>
          <div class="node-name">${cycle.name}</div>
        `;
        
        node.addEventListener('click', function() {
          // Remove active class and pulse from all nodes
          const nodes = document.querySelectorAll('.node');
          nodes.forEach(n => {
            n.classList.remove('active');
            const existingPulse = n.querySelector('.node-pulse');
            if (existingPulse) {
              n.removeChild(existingPulse);
            }
          });
          
          // Add active class and pulse to clicked node
          this.classList.add('active');
          const pulse = document.createElement('div');
          pulse.className = 'node-pulse';
          pulse.style.backgroundColor = emotionCycleData[index].color;
          this.appendChild(pulse);
          
          // Show details
          showDetail(cycle.id);
          
          // Activate corresponding chart point
          activateChartPoint(cycle.id);
        });
        
        nodesContainer.appendChild(node);
      });
    }
    
    // Render chart points
    function renderChartPoints() {
      const chartPoints = document.querySelector('.chart-points');
      const chartTooltip = document.querySelector('.chart-tooltip');
      
      const points = [
        { id: 1, x: 100, y: 180 },
        { id: 2, x: 300, y: 120 },
        { id: 3, x: 500, y: 50 },
        { id: 4, x: 700, y: 70 },
        { id: 5, x: 900, y: 200 }
      ];
      
      points.forEach((point, index) => {
        const cycle = emotionCycleData[index];
        const pointElement = document.createElement('div');
        pointElement.className = 'chart-point';
        pointElement.dataset.id = cycle.id;
        pointElement.style.backgroundColor = cycle.color;
        pointElement.style.left = `${point.x / 10}%`;
        pointElement.style.top = `${point.y / 3}%`;
        
        if (index === 0) {
          pointElement.classList.add('active');
        }
        
        pointElement.addEventListener('mouseover', function(e) {
          chartTooltip.innerHTML = cycle.name;
          chartTooltip.style.left = `${e.pageX - chartTooltip.offsetWidth / 2}px`;
          chartTooltip.style.top = `${e.pageY - chartTooltip.offsetHeight - 10}px`;
          chartTooltip.classList.add('show');
        });
        
        pointElement.addEventListener('mouseout', function() {
          chartTooltip.classList.remove('show');
        });
        
        pointElement.addEventListener('click', function() {
          // Remove active class from all points
          document.querySelectorAll('.chart-point').forEach(p => {
            p.classList.remove('active');
          });
          
          // Add active class to clicked point
          this.classList.add('active');
          
          // Show details
          showDetail(cycle.id);
          
          // Activate corresponding node
          activateNode(cycle.id);
        });
        
        chartPoints.appendChild(pointElement);
      });
    }
    
    // Show detail for selected cycle
    function showDetail(id) {
      const detailCard = document.querySelector('.detail-card');
      const cycle = emotionCycleData.find(c => c.id === parseInt(id));
      
      if (!cycle) return;
      
      const iconClass = getIconClass(cycle.icon);
      
      detailCard.innerHTML = `
        <div class="detail-header">
          <div class="detail-icon-container" style="background-color: ${cycle.color}">
            <i class="detail-icon ${iconClass}"></i>
          </div>
          <div class="detail-title">
            <h2 class="detail-name">${cycle.name}</h2>
            <div class="detail-alias">${cycle.alias}</div>
          </div>
        </div>
        
        <div class="detail-description">
          ${cycle.description}
        </div>
        
        <div class="detail-content">
          <div class="detail-section">
            <h3 class="detail-section-title">
              <i class="fas fa-list-ul" style="color: ${cycle.color}"></i>
              市场特征
            </h3>
            <ul class="detail-list">
              ${cycle.features.map(feature => `<li>${feature}</li>`).join('')}
            </ul>
          </div>
          
          <div class="detail-section">
            <h3 class="detail-section-title">
              <i class="fas fa-lightbulb" style="color: ${cycle.color}"></i>
              操作策略
            </h3>
            <ul class="detail-list">
              ${cycle.strategies.map(strategy => `<li>${strategy}</li>`).join('')}
            </ul>
          </div>
        </div>
      `;
      
      // Apply animation
      detailCard.classList.remove('show');
      setTimeout(() => {
        detailCard.classList.add('show');
      }, 50);
    }
    
    // Activate node based on chart point
    function activateNode(id) {
      const nodes = document.querySelectorAll('.node');
      const cycle = emotionCycleData.find(c => c.id === parseInt(id));
      
      nodes.forEach(node => {
        node.classList.remove('active');
        const existingPulse = node.querySelector('.node-pulse');
        if (existingPulse) {
          node.removeChild(existingPulse);
        }
        
        if (parseInt(node.dataset.id) === parseInt(id)) {
          node.classList.add('active');
          const pulse = document.createElement('div');
          pulse.className = 'node-pulse';
          pulse.style.backgroundColor = cycle.color;
          node.appendChild(pulse);
        }
      });
    }
    
    // Activate chart point based on node
    function activateChartPoint(id) {
      const points = document.querySelectorAll('.chart-point');
      
      points.forEach(point => {
        point.classList.remove('active');
        
        if (parseInt(point.dataset.id) === parseInt(id)) {
          point.classList.add('active');
        }
      });
    }
    
    // Helper function to map icon names to Font Awesome classes
    function getIconClass(iconName) {
      const iconMap = {
        'thermometer-snow': 'fas fa-snowflake',
        'seedling': 'fas fa-seedling',
        'chart-line-up': 'fas fa-chart-line',
        'rocket': 'fas fa-rocket',
        'wave-sine': 'fas fa-water'
      };
      
      return iconMap[iconName] || 'fas fa-circle';
    }
    
    // Add animated market data visualization
    function createMarketVisualizer() {
      const visualizerContainer = document.createElement('div');
      visualizerContainer.className = 'market-visualizer';
      visualizerContainer.style.marginTop = '3rem';
      visualizerContainer.style.position = 'relative';
      visualizerContainer.style.height = '200px';
      visualizerContainer.style.overflow = 'hidden';
      visualizerContainer.style.borderRadius = '8px';
      visualizerContainer.style.backgroundColor = '#f1f5f9';
      
      const marketTitle = document.createElement('h3');
      marketTitle.textContent = '市场活跃度实时模拟';
      marketTitle.style.textAlign = 'center';
      marketTitle.style.marginBottom = '1rem';
      
      document.querySelector('.cycle-container').appendChild(marketTitle);
      document.querySelector('.cycle-container').appendChild(visualizerContainer);
      
      // Create dots representing market activity
      const dotCount = 50;
      const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#6b7280'];
      
      for (let i = 0; i < dotCount; i++) {
        const dot = document.createElement('div');
        dot.style.position = 'absolute';
        dot.style.width = `${Math.random() * 8 + 4}px`;
        dot.style.height = dot.style.width;
        dot.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
        dot.style.borderRadius = '50%';
        dot.style.opacity = Math.random() * 0.7 + 0.3;
        dot.style.left = `${Math.random() * 100}%`;
        dot.style.top = `${Math.random() * 100}%`;
        dot.style.transition = 'all 2s ease-in-out';
        
        visualizerContainer.appendChild(dot);
        
        // Animate dots
        setInterval(() => {
          dot.style.left = `${Math.random() * 100}%`;
          dot.style.top = `${Math.random() * 100}%`;
          dot.style.opacity = Math.random() * 0.7 + 0.3;
        }, Math.random() * 3000 + 2000);
      }
    }
    
    // Add interactive cycle switcher
    function createCycleSwitcher() {
      const switcherContainer = document.createElement('div');
      switcherContainer.className = 'cycle-switcher';
      switcherContainer.style.display = 'flex';
      switcherContainer.style.justifyContent = 'center';
      switcherContainer.style.gap = '1rem';
      switcherContainer.style.marginTop = '2rem';
      switcherContainer.style.flexWrap = 'wrap';
      
      emotionCycleData.forEach(cycle => {
        const button = document.createElement('button');
        button.className = 'cycle-button';
        button.dataset.id = cycle.id;
        button.style.backgroundColor = cycle.color;
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '8px';
        button.style.padding = '0.75rem 1.5rem';
        button.style.fontSize = '1rem';
        button.style.fontWeight = 'bold';
        button.style.cursor = 'pointer';
        button.style.transition = 'all 0.3s';
        button.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
        
        const iconClass = getIconClass(cycle.icon);
        
        button.innerHTML = `
          <i class="${iconClass}" style="margin-right: 0.5rem;"></i>
          ${cycle.name}
        `;
        
        button.addEventListener('mouseover', function() {
          this.style.transform = 'translateY(-3px)';
          this.style.boxShadow = '0 6px 12px rgba(0, 0, 0, 0.15)';
        });
        
        button.addEventListener('mouseout', function() {
          this.style.transform = 'translateY(0)';
          this.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
        });
        
        button.addEventListener('click', function() {
          const id = parseInt(this.dataset.id);
          showDetail(id);
          activateNode(id);
          activateChartPoint(id);
          
          // Highlight active button
          document.querySelectorAll('.cycle-button').forEach(btn => {
            btn.style.opacity = '0.7';
          });
          this.style.opacity = '1';
        });
        
        switcherContainer.appendChild(button);
      });
      
      // Set first button as active
      switcherContainer.querySelector('.cycle-button').style.opacity = '1';
      document.querySelectorAll('.cycle-button').forEach((btn, i) => {
        if (i > 0) btn.style.opacity = '0.7';
      });
      
      document.querySelector('.cycle-container').appendChild(switcherContainer);
    }
    
    // Add market data table
    function createMarketDataTable() {
      const tableContainer = document.createElement('div');
      tableContainer.className = 'market-data-table';
      tableContainer.style.marginTop = '3rem';
      tableContainer.style.backgroundColor = 'white';
      tableContainer.style.borderRadius = '16px';
      tableContainer.style.padding = '1.5rem';
      tableContainer.style.boxShadow = '0 10px 25px rgba(0, 0, 0, 0.05)';
      
      const tableTitle = document.createElement('h3');
      tableTitle.textContent = '各周期市场特征对比';
      tableTitle.style.marginBottom = '1.5rem';
      tableTitle.style.borderBottom = '1px solid #e2e8f0';
      tableTitle.style.paddingBottom = '1rem';
      
      const table = document.createElement('div');
      table.style.display = 'grid';
      table.style.gridTemplateColumns = 'repeat(6, 1fr)';
      table.style.gap = '1rem';
      table.style.fontSize = '0.9rem';
      
      // Headers
      const headers = ['特征', '冰点期', '启动期', '发酵期', '高峰期', '退潮期'];
      headers.forEach(header => {
        const cell = document.createElement('div');
        cell.textContent = header;
        cell.style.fontWeight = 'bold';
        cell.style.padding = '0.5rem';
        table.appendChild(cell);
      });
      
      // Data rows
      const rows = [
        ['涨停数量', '极少', '少量', '增多', '大量', '减少'],
        ['连板高度', '1-2板', '3-4板', '4-5板', '6-10板', '下降'],
        ['赚钱效应', '几乎没有', '零星出现', '逐渐扩散', '普遍存在', '逐渐减弱'],
        ['操作难度', '极高', '较高', '中等', '较低', '逐渐增加'],
        ['资金状态', '观望为主', '试探进场', '加速进场', '全面入场', '加速撤离'],
        ['风险程度', '小幅回调', '低风险', '中等风险', '高风险', '极高风险']
      ];
      
      rows.forEach(row => {
        row.forEach((cell, i) => {
          const cellElement = document.createElement('div');
          cellElement.textContent = cell;
          cellElement.style.padding = '0.5rem';
          
          if (i > 0) {
            const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#6b7280'];
            cellElement.style.borderLeft = `2px solid ${colors[i-1]}`;
            cellElement.style.color = colors[i-1];
          }
          
          table.appendChild(cellElement);
        });
      });
      
      tableContainer.appendChild(tableTitle);
      tableContainer.appendChild(table);
      
      document.querySelector('.cycle-container').appendChild(tableContainer);
    }
  </script>
</body>
</html>
1 个赞

打板来的快去得也快

1 个赞

进来看看。

A股:只有冰点

这我真的不敢用,被量化来回收割,还轮得到我赚钱?

这是基本的情绪周期吧?懂了不会看盘也没用啊

此话题已在最后回复的 30 天后被自动关闭。不再允许新回复。