openlayer 轨迹线路

一、想要的效果

1、初始效果


1675394076042.png

2、运动时效果


1675394662914.png

二、准备线路坐标点

可以使用地图拾取器系统

coordinates: [
            [113.53641592772293, 34.82798925347799],
            [113.55119787497061, 34.82789388575531],
            [113.55920874272088, 34.825128232275],
            [113.56130682738113, 34.821408898948555],
            [113.5621651342659, 34.818071041751516],
            [113.56197440405923, 34.808915766567786],
            [113.59115683814126, 34.80662694646218],
            [113.60660636206704, 34.81158605756411]
          ]

绘制轨迹

this.line = new LineString(this.options.coordinates) //  直线
    // 线路所有坐标点坐标
    let coordinates = this.options.coordinates
    this.lineFeature = new ol.Feature({ //  直线线路
      type: 'line',
      geometry: this.line
    })
    this.startMarker = new ol.Feature({ //  开始点位
      type: 'icon',
      geometry: new ol.Point(this.line.getFirstCoordinate())
    })
    this.endMarker = new ol.Feature({ //  结束点位
      type: 'icon',
      geometry: new ol.Point(this.line.getLastCoordinate())
    })
    /**
     * 经停点
     */

    for (let i = 0; i < coordinates.length; i++) {
      let s = new ol.Feature({
        type: 'stop',
        geometry: new ol.Point(coordinates[i])
      })
      this.stopMakers.push(s)
    }

    this.movePoint = this.startMarker.getGeometry().clone() // 移动点
    this.movePointMarker = new ol.Feature({ //  移动点feature
      type: 'geoMarker',
      geometry: this.movePoint
    })

    let routeFeature = [...[this.lineFeature, this.movePointMarker, this.startMarker, this.endMarker], ...this.stopMakers]

    this.trajectoryLayer = new ol.VectorLayer({ // 添加图层
      source: new ol.VectorSource({
        features: routeFeature,
        className: 'TrajectoryLayer',
        properties: this.options.layerProperties
      }),
      style: (feature) => {
        return this.routeStyle[feature.get('type')]
      }

    })

    this.map.addLayer(this.trajectoryLayer)

三、设置线条、经停点、导航样式

let coordinates = this.options.coordinates
    let sourcePoint = coordinates[this.currentIndex]
    let targetPoint = coordinates[this.currentIndex + 1]

    this.routeStyle = { // 定义线路样式
      line: new ol.Style({
        stroke: new ol.Stroke({
          width: this.options.lineStyle.width,
          color: this.options.lineStyle.color
        })
      }),

      geoMarker: new ol.Style({
        image: new ol.Icon({
          src: this.options.navStyle.url,
          scale: this.options.navStyle.scale,
          rotateWithView: false,
          rotation: this.getRotation(sourcePoint, targetPoint)

        })

      }),
      stop: new ol.Style({
        image: new ol.CircleStyle({
          radius: this.options.stopStyle.radius - 2,
          fill: new ol.Fill({
            // width: this.options.routeStyle.width,
            color: this.options.stopStyle.color
          }),
          stroke: new ol.Stroke({
            width: (this.options.stopStyle.radius - 2) / 2,
            color: this.options.stopStyle.color
          })
        })
      })
    }

四、引入 动画开关、进度条、速度 控制器

this.playbarControl = new PlaybarControl(this.map, _config, (e, pos) => { //  添加播放停止动画控制器
        if (pos === 100) { // 在尾端
          this.movePoint.setCoordinates(this.options.coordinates[0]) // 设置移动点当前位置
          this.movePointMarker.setGeometry(this.movePoint)
          this.distance = 0
          this.currentIndex = 0
          // this.redrawMovePoing(this.options.coordinates[0], this.options.coordinates) // 设置移动点到一个点位
        }
        this.setAnimation(e)
      }, (e) => {
        this.setAnimationSpeed(e)
      })
      this.map.addControl(this.playbarControl)

源码

1、 目录结构


1675394327774.png

2、config.js 默认配置项源码


import IconNav from '@/assets/images/map/marker-icon/icon-nav.png'

/**
 * 轨迹默认配置项
 * config:{
*     type: 'TrajectoryLayer',
      layerProperties:{ // 图层属性配置
        name:'TrajectoryLayer',
        id:'TrajectoryLayer'
      }||undefined // layer 属性
      coordinates: [], // 道路点位坐标
      layerStyle: [],// 定位点样式
      action: [{ // marks 事件数组,目前仅支持click 模式
        type: 'click',
        hitTolerance: 10, // 命中容差
        handle: this.handleMarkHover //  事件回调函数
      }]
 */
export const defaultConfig = {
  type: 'TrajectoryLayer',
  layerProperties: {
    name: 'TrajectoryLayer',
    id: 'TrajectoryLayer'
  },
  coordinates: [], // 道路点位坐标
  layerStyle: [], // 样式配置
  actions: [], //  事件配置
  lineStyle: { // 道路样式
    width: 5,
    color: '#FF0C00'
  },
  navStyle: { // 导航图片样式
    scale: 1,
    url: IconNav
  },
  stopStyle: { // 经停点样式
    radius: 8,
    color: '#ff8f00'
  },
  speed: 80,
  tolerance: 10, // 容差,最小10
  imageAngle: 1.5 //  图片自身旋转角度

}

2、自定义控制器源码

import { Control } from 'ol/control'
/**
 * 播放控件
 */
export class PlaybarControl extends Control {
  /**
   * @param {Object} [opt_options] Control options.
   */
  constructor (_map, _options, _playCallBack, _speedSetCallBack) {
    const options = _options //  配置项

    let playContainer = document.createElement('div')
    playContainer.id = 'ol-play-control'
    playContainer.className = 'ol-play-control'

    super({
      element: playContainer,
      target: options.target
    })
    this.currentPos = 0
    this.palyStatus = false // 默认暂停
    this.currentSpeed = 1
    this.progressEle = null //  进度条
    this.progressTipContianerEle = null //  提示框
    this.playOptEle = null //  暂停、开始按钮
    this.initPlay(playContainer)
    this.playCallBack = _playCallBack
    this.speedSetCallBack = _speedSetCallBack

    // this.actionOptions = actionOption
  }

  setProgressEleWidth (pos) {
    if (this.progressEle && this.progressTipContianerEle) {
      this.currentPos = pos

      if (!this.palyStatus) {
        this.palyStatus = true
        this.playOptEle.className = `${this.palyStatus ? 'ol-opt-playing ol-play-opt' : 'ol-play-opt'}`
        // console.log('状态是false 时,this.palyStatus', this.palyStatus, pos, Math.round(pos))
      }

      // console.log('this.palyStatus', this.palyStatus)
      if (this.currentPos > 0 && Math.round(pos) < 100) {
        this.progressEle.style.width = this.currentPos + '%'
      } else if (this.currentPos === 0 || this.currentPos < 0) {
        this.progressEle.style.width = '0px'
      } else if (Math.round(pos) === 100 || Math.round(pos) > 100) {
        this.currentPos = 100
        this.progressEle.style.width = '100%'
        this.palyStatus = false
        this.playOptEle.className = `${this.palyStatus ? 'ol-opt-playing ol-play-opt' : 'ol-play-opt'}`
        // console.log('进入', this.palyStatus)
      }

      if (this.currentPos === 0 || this.currentPos < 0) {
        this.progressTipContianerEle.style.display = 'none'
      } else {
        this.progressTipContianerEle.style.display = 'block'
        let width = this.progressTipContianerEle.clientWidth
        // console.log('width=', width)
        this.progressTipContianerEle.style.left = width / 2 + 'px'
        // if (this.currentPos > 0 && this.currentPos < 100) {
        //   this.progressTipContianerEle.style.left = width / 2 + 'px'
        // } else if (this.currentPos === 0 || this.currentPos < 0) {
        //   this.progressTipContianerEle.style.left = '100px'
        // } else if (Math.round(pos) === 100 || Math.round(pos) > 100) {
        //   this.currentPos = 100
        //   this.progressEle.stylestyle.left = width + 'px'
        //   this.palyStatus = false
        // }
      }
    }
  }

  /**
   * 播放
   */
  initPlay (playContainer) {
    this.initPlayOptsContainer(playContainer)
    this.initProgressContainer(playContainer)
    this.initSpeedContainer(playContainer)
  }

  /**
   * 初始化播放、暂停box
   */
  initPlayOptsContainer (playContainer) {
    this.playOptEle = document.createElement('div') //  开启、停止按钮
    this.playOptEle.className = `${this.palyStatus ? 'ol-opt-playing ol-play-opt' : 'ol-play-opt'}`
    playContainer.appendChild(this.playOptEle)
    this.playOptEle.addEventListener('click', (e) => {
      // console.log('ss')
      this.palyStatus = !this.palyStatus
      this.playOptEle.className = `${this.palyStatus ? 'ol-opt-playing ol-play-opt' : 'ol-play-opt'}`
      // console.log('this.onPlay', this.playCall)
      this.playCallBack(this.palyStatus, this.currentPos)
    })
  }

  /**
   * 初始化进度条容器box
   * @param {*} playContainer
   */
  initProgressContainer (playContainer) {
    let progressContainerEle = document.createElement('div') //  进度条box容器
    progressContainerEle.className = 'ol-progress-container'
    this.progressEle = document.createElement('div') //  进度条容器
    this.progressEle.className = 'ol-progress'
    progressContainerEle.appendChild(this.progressEle) // 将进度条容器 写入 进度条box容器

    this.progressTipContianerEle = document.createElement('div') //  进度提示容器
    this.progressTipContianerEle.className = 'ol-progress-tip-container'
    this.progressTipContianerEle.innerText = '当前速度是'
    let speedTipEle = document.createElement('div') //  速度容器
    speedTipEle.className = 'ol-progress-tip-speed'
    speedTipEle.innerText = '0km/h'

    this.progressEle.appendChild(this.progressTipContianerEle) // 将速度文字提示ele 写入 进度条

    this.progressTipContianerEle.appendChild(speedTipEle) // 将速度容器ele 写入 进度条
    playContainer.appendChild(progressContainerEle) // 进度条box容器 play控制器

    // setTimeout(() => {
    //   let width = this.progressTipContianerEle.clientWidth
    //   this.progressTipContianerEle.style.left = width / 2 + 'px'
    //   console.log('width', width)
    // }, 100)
  }

  /**
   * 初始化速度设置容器box
   * @param {*} playContainer
   */
  initSpeedContainer (playContainer) {
    let playSpeed = document.createElement('select') //  播放速度样式
    playSpeed.className = 'ol-speed'
    playSpeed.options.add(new Option('1x', 1))// 兼容所有浏览器
    playSpeed.options.add(new Option('1.5x', 1.5))// 兼容所有浏览器
    playSpeed.options.add(new Option('2x', 2))// 兼容所有浏览器
    playContainer.appendChild(playSpeed)
    playSpeed.addEventListener('change', (e) => {
      // console.log('选择 e', e, e.target.value)
      this.speedSetCallBack(e.target.value)
    })
  }
}

3、全局添加控制器样式

#ol-play-control{
  padding:10px 15px;
  background: rgba(0,0,0,.5);
  color:#fff;
  position: absolute;
  bottom: 10px;
  right:10px;
  border-radius: 10px;
  left: 10px;
  @include flexLayout($vertical:center);
  font-size: 12px;
  .ol-play-opt{
    position: relative;
    width: 30px;
    height: 30px;
    cursor: pointer;
    &::before{
      content:'';
      position: absolute;
      top:5px;
      left:9px;
      border-style: solid;
      border-color: transparent;
      border-width: 12px 0 12px 12px;
      border-left-color: #fff;
    }
    &.ol-opt-playing{
      &::before,&::after{
        content:'';
        position: absolute;
        width:4px;
        // height: 10px;
        top:5px;
        bottom:5px;
        background: #fff;
        border-radius: 5px;
      }
      &::before{
        left:8px;
        border:0
      }
      &::after{
        right:8px
      }
    }
  }
  .ol-progress-container{
    flex:1;
    margin:0 5px;
    background: rgba(#fff,.5);
    height: 5px;
    position: relative;
    
    .ol-progress{
      height: 100%;
      background: #fff;
      width: 0%;
      position: relative;
      @include flexLayout($vertical:center,$horizontal:flex-end);
      .ol-progress-tip-container{
       display: none;
        top:-30px;
        left:65px;
        width:auto;
        min-width: 130px;
        position: relative;
        // background: #fff;
        padding:10px;
        // padding-left: 0;
        background-color: #fff;
        // border-radius: 5px;
        color:#000;
        font-weight: bold;
        border-radius: 8px;
        border-radius: 8px;
        box-shadow: 0 0 5px rgba(#000,.4);
        .ol-progress-tip-speed{
          display: inline-block;
          color:#4969DC;
          margin-left:10px;
        }
        &::after{
          content:"";
          position: absolute;
          bottom:-22px;
          left:50%;
          margin-left: -12px;
          border-style: solid;
          border-color: transparent;
          border-width: 12px;
          border-top-color: #fff;
        }
      }
     
    }
   
  }
  .ol-speed{
    border:1px solid rgba(#fff,.8);
    background-color: transparent;
    color:#fff;
    cursor: pointer;
    border-radius: 5px;
    margin-left: 5px;
    text-align: center;
    padding:2px 0px;
    font-size:14px;
    position: relative;
    option{
      color:#000;
      border-color:rgba(#000,.4)
    }
  }
}

4、轨迹类封装

import * as ol from '../openLayer'
import { getVectorContext } from 'ol/render.js'
import { getDistance } from 'ol/sphere.js'
import { PlaybarControl } from './play'
import { defaultConfig } from './config'
import { deepAssign } from '@/utils'
import { LineString } from 'ol/geom'
/**
 * 轨迹类
 */
export default class OlMapTrajectory {
  constructor (_map, _config) {
    this.map = _map
    this.options = deepAssign(defaultConfig, _config) //  配置项
    this.line = null // 直线
    this.lineFeature = null // 直线feature
    this.startMarker = null // 开始点位
    this.endMarker = null // 结束点位
    this.stopMakers = [] // 经停点
    this.routeStyle = null // 轨迹样式
    this.movePoint = null // 移动点
    this.movePointMarker = null //  移动点feature
    this.moveFeature = null // 移动feature
    this.lastTime = null //  最后一次移动时间
    this.animating = false // 是否正在移动,false-> 停止 true-> 移动
    this.trajectoryLayer = null //  openlayer 对象
    this.speed = this.options.speed //  移动速度
    this.distance = 0 // 已经移动的距离
    this.currentIndex = 0 //  当前坐标点下标
    this.lineAllLength = 0 //  轨迹总长度
    this.lineSegmentLength = [] // 线段直接的距离
    this.vectorContext = null // canvas 上下文
    this.tolerance = this.options.tolerance < 10 ? 10 : this.options.tolerance // 容差,最小为10
    this.imageAngle = this.options.imageAngle //  图片自身旋转角度
    if (this.options.coordinates.length > 0) {
      this.setRouteStyle() //  设置轨迹样式
      this.drwaRoute() //  绘制线条
      this.initMoveFeature() //  初始化移动moveFeature
      this.lineAllLength = this.getLineAllLength() //  获取线条总长度
      this.playbarControl = new PlaybarControl(this.map, _config, (e, pos) => { //  添加播放停止动画控制器
        if (pos === 100) { // 在尾端
          this.movePoint.setCoordinates(this.options.coordinates[0]) // 设置移动点当前位置
          this.movePointMarker.setGeometry(this.movePoint)
          this.distance = 0
          this.currentIndex = 0
          // this.redrawMovePoing(this.options.coordinates[0], this.options.coordinates) // 设置移动点到一个点位
        }
        this.setAnimation(e)
      }, (e) => {
        this.setAnimationSpeed(e)
      })
      this.map.addControl(this.playbarControl)
    }
  }

  /**
   * 重新绘制移动点位置和样式
   */
  redrawMovePoing (currentCoordinate, coordinates, vectorContext) {
    this.movePoint.setCoordinates(currentCoordinate) // 设置移动点当前位置
    // console.log('this.currentIndex=', this.currentIndex, this.getRotation(coordinates[this.currentIndex], coordinates[this.currentIndex + 1]))
    let rotation = this.getRotation(coordinates[this.currentIndex], coordinates[this.currentIndex + 1])
    if (rotation > 3) {
      rotation = rotation - this.imageAngle
    }
    let movePointStyle = new ol.Style({
      image: new ol.Icon({
        src: this.options.navStyle.url,
        scale: this.options.navStyle.scale,
        rotateWithView: false,
        rotation: rotation

      })
    })
    this.vectorContext.setStyle(movePointStyle) //  移动时重新计算线路角度
    this.vectorContext.drawGeometry(this.movePoint)
  }

  /**
 * 得到总线路长度
 * @returns
 */
  getLineAllLength () {
    let coordinates = this.options.coordinates
    let length = 0
    for (let i = 0; i < coordinates.length - 1; i++) {
      // console.log(coordinates[i], i)
      length = length + this.formatLength(coordinates[i], coordinates[i + 1])
      this.lineSegmentLength.push(parseInt(length))
    }
    console.log('this.lineSegmentLength', this.lineSegmentLength)
    return length
  }

  /**
   * 设置动画的速度
   * @param {*} e
   */
  setAnimationSpeed (e) {
    this.speed = e * this.options.speed
    // this.initMoveFeature()
  }

  /**
   * 开启或者停止动画
   * @param {*} e
   */
  setAnimation (e) {
    // console.log('e', e)
    this.animating = e
    if (this.animating) {
      this.startAnimation()
    } else {
      this.stopAnimation()
    }
  }
  /**
   * 开始动画
   */

  startAnimation () {
    console.log('开始移动')
    this.animating = true
    this.lastTime = new Date().getTime() /** 开始时的时间 */
    this.trajectoryLayer.on('postrender', this.moveFeature)
    // hide geoMarker and trigger map render through change event
    this.movePointMarker.setGeometry(null)
  }

  /**
 * 停止动画
 */
  stopAnimation () {
    this.animating = false

    // Keep marker at current animation position
    this.movePointMarker.setGeometry(this.movePoint)
    this.trajectoryLayer.un('postrender', this.moveFeature)
  }

  /**
 * 获取线段长度
 * @param {*} line
 * @returns
 */
  formatLength (sourcePoint, targetPoint) {
    const length = getDistance(sourcePoint, targetPoint)
    let output
    if (length > 1000) {
      output = Math.round((length / 1000) * 100) / 100 + ' km'
    } else {
      output = Math.round(length * 100) / 100 + ' m'
    }
    return getDistance(sourcePoint, targetPoint)
  };
  /**
  * 根据坐标获取角度数,以正上方为0度作为参照
  * @param  sourcePoint 源点
  * @param  targetPoint 目标点
  */

  getRotation (sourcePoint, targetPoint) {
    try {
      return -Math.atan2(targetPoint[1] - sourcePoint[1], targetPoint[0] - sourcePoint[0]) + this.imageAngle //  计算导航图标旋转角度
    } catch (error) {
      console.log(error, sourcePoint, targetPoint)
    }
  }

  /**
   * 初始化移动
   */
  initMoveFeature () {
    if (this.moveFeature) {
      console.log('已存在', this.moveFeature)
    } else {
      let routeLength = this.options.coordinates.length
      let coordinates = this.options.coordinates
      let moveLength = 0
      let preCoordinate = this.options.coordinates[0]

      this.moveFeature = (e) => {
        // console.log('moveFeaturee=', e)
        this.vectorContext = getVectorContext(e) // //HTML5 Canvas context,ol.render.canvas.Immediate的对象
        const speed = parseInt(this.speed)
        let frameState = e.frameState // freme 的状态
        const time = frameState.time
        const elapsedTime = time - this.lastTime
        if (this.distance === 0) {
          preCoordinate = this.options.coordinates[0]
          moveLength = 0
          this.lastTime = new Date().getTime()
        }
        // console.log('frameStateindex=', frameState.index)
        this.distance = (this.distance + (speed * elapsedTime) / 1e6) % 2

        // console.log('this.currentIndex=', this.currentIndex)
        // console.log('distance', distance)
        this.lastTime = time
        const currentCoordinate = this.line.getCoordinateAt(
          this.distance > 1 ? 2 - this.distance : this.distance
        )
        moveLength = moveLength + this.formatLength(preCoordinate, currentCoordinate) // 计算移动过的距离
        preCoordinate = currentCoordinate //  缓存上一个定位点
        // console.log('moveLength=', currentCoordinate, moveLength)
        // console.log('moveLength=', moveLength, this.lineSegmentLength.indexOf(moveLength))

        let progressWidth = (moveLength / this.lineAllLength) * 100 //  计算计算进度条的位置
        // console.log('progressWidth=', progressWidth)
        this.playbarControl.setProgressEleWidth(progressWidth) //  设置进度条的位置
        this.getCurrentIndex(moveLength, coordinates) // 计算移动点在坐标数组里的下标
        this.redrawMovePoing(currentCoordinate, coordinates) // 从新绘制移动点的位置
        this.map.render() // 继续动画效果
        if (this.distance > 1) {
          this.stopAnimation()
        }
      }
    }
  }

  /**
   * 计算当前坐标点下标,
   * moveLength
   */
  getCurrentIndex (moveLength, coordinates) {
    let filtCoordinate = this.lineSegmentLength.filter(item => {
      let tempMoveLength = parseInt(moveLength)
      let tempItem
      // console.log(tempMoveLength, item)
      if (tempMoveLength > item - this.tolerance && tempMoveLength < item + this.tolerance) {
        tempItem = item
      }
      return tempItem
    })
    // console.log('filtCoordinate=', filtCoordinate)
    if (filtCoordinate.length > 0) {
      this.currentIndex = this.lineSegmentLength.indexOf(filtCoordinate[0])
      // console.log('this.currentIndex=', this.currentIndex)
    }
    if (this.currentIndex >= coordinates.length) {
      this.currentIndex = coordinates.length - 1
    }
  }

  /**
   * 道路轨迹
   */
  drwaRoute () {
    this.line = new LineString(this.options.coordinates) //  直线
    // 线路所有坐标点坐标
    let coordinates = this.options.coordinates
    this.lineFeature = new ol.Feature({ //  直线线路
      type: 'line',
      geometry: this.line
    })
    this.startMarker = new ol.Feature({ //  开始点位
      type: 'icon',
      geometry: new ol.Point(this.line.getFirstCoordinate())
    })
    this.endMarker = new ol.Feature({ //  结束点位
      type: 'icon',
      geometry: new ol.Point(this.line.getLastCoordinate())
    })
    /**
     * 经停点
     */

    for (let i = 0; i < coordinates.length; i++) {
      let s = new ol.Feature({
        type: 'stop',
        geometry: new ol.Point(coordinates[i])
      })
      this.stopMakers.push(s)
    }

    this.movePoint = this.startMarker.getGeometry().clone() // 移动点
    this.movePointMarker = new ol.Feature({ //  移动点feature
      type: 'geoMarker',
      geometry: this.movePoint
    })

    let routeFeature = [...[this.lineFeature, this.movePointMarker, this.startMarker, this.endMarker], ...this.stopMakers]

    this.trajectoryLayer = new ol.VectorLayer({ // 添加图层
      source: new ol.VectorSource({
        features: routeFeature,
        className: 'TrajectoryLayer',
        properties: this.options.layerProperties
      }),
      style: (feature) => {
        return this.routeStyle[feature.get('type')]
      }

    })

    this.map.addLayer(this.trajectoryLayer)
  }

  /**
   * 设置轨迹线路样式
   */
  setRouteStyle () {
    let coordinates = this.options.coordinates
    let sourcePoint = coordinates[this.currentIndex]
    let targetPoint = coordinates[this.currentIndex + 1]

    this.routeStyle = { // 定义线路样式
      line: new ol.Style({
        stroke: new ol.Stroke({
          width: this.options.lineStyle.width,
          color: this.options.lineStyle.color
        })
      }),

      geoMarker: new ol.Style({
        image: new ol.Icon({
          src: this.options.navStyle.url,
          scale: this.options.navStyle.scale,
          rotateWithView: false,
          rotation: this.getRotation(sourcePoint, targetPoint)

        })

      }),
      stop: new ol.Style({
        image: new ol.CircleStyle({
          radius: this.options.stopStyle.radius - 2,
          fill: new ol.Fill({
            // width: this.options.routeStyle.width,
            color: this.options.stopStyle.color
          }),
          stroke: new ol.Stroke({
            width: (this.options.stopStyle.radius - 2) / 2,
            color: this.options.stopStyle.color
          })
        })
      })
    }
  }

  updateTrajectory (_map, _config) {
    this.map = _map
    this.options = deepAssign(defaultConfig, _config) //  配置项
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容