(第四天)HTML5之Canvas图形绘制:图形、moveTo&lineTo&beginPath&closePath、贝塞尔曲线、渐变、缩放平移旋转及上下文状态保存及还原

Canvas图形绘制 <small>API参考</small>


  • 开始创建路径ctx.beginPath(),通过此方法避免前面的图形重复绘制。
  • (以圆形为例)创建圆形路径ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
    <small>
    • x:圆心X坐标;
    • y:圆心Y坐标;
    • radius:圆半径;
    • startAngle:起始弧度,一般为0;根据情况而定;
    • endAngle:结束弧度,2 * Math.PI 表示360°;
    • anticlockwise:可选值;默认为false,即顺时针绘制;如果为 true,逆时针绘制圆弧。
      </small>
  • 关闭路径ctx.closePath():将笔点返回到当前子路径起始点的方法;它尝试从当前点到起始点绘制一条直线。 如果图形已经是封闭的或者只有一个点,那么此方法不会做任何操作。

<small>若不需要封闭非整圆,请勿使用该方法;否则会在非整圆终点至起始点绘制一条直线。</small>

  • 设置画笔绘制样式<small>(填充ctx.fillStyle;划线ctx.strokeStyle,线宽ctx.lineWidth。)</small>
  • 开始绘制图形<small>(填充ctx.fill(),划线ctx.stroke())</small>
<!--canvas arc demo-->
    <script>
    "use strict"
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        // 获得一个2d渲染上下文
        var ctx = canvas.getContext("2d");
        for (var i = 1; i < 10; i++) {
            // 创建路径;如果不重新创建路径,存在的图形路径会重复绘制
            ctx.beginPath();
            //创建图形路径; 
            ctx.arc(50 * i, 50 * i, 20 * i, 0, 2 * Math.PI, true);
            //闭合路径;
            ctx.closePath();
            //设置填充颜色
            ctx.fillStyle = "rgba(255,0,0,0.25)";
            //开始填充图形
            ctx.fill();
            // 划笔样式
            ctx.strokeStyle = "#ff0";
            // 划线渲染
            ctx.stroke();
        }
    }
    </script>
    <canvas id="canvas" width="1000" height="1000"></canvas>
</body>

moveTo & lineTo & beginPath & closePath


  • moveTo(x,y):将画笔移动到x,y坐标(该坐标为第一个子路径的终点);
  • lineTo(x,y):直线连接子路径的终点到x,y坐标(只连接,不绘制);
  • beginPath():通过清空子路径列表开始一个新路径的方法。 当你想创建一个新的路径时,调用此方法;
  • closePath():从当前点到起始点绘制一条直线。 如果图形已经是封闭的或者只有一个点,那么此方法不会做任何操作。

重点:没有子路径时(即没有moveTo或lineTo时),lineTo作用与moveTo相同

代码示例:
<body>
<script>
    window.onload = function () {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var context = canvas.getContext("2d");
        //创建路径
        context.beginPath();
        //移动画笔 1.将画笔移动到50,50的坐标
        context.moveTo(50, 50);
        //画第一条线 2.从moveTo的50,50到lineTo的400,50坐标划一条线
        context.lineTo(100, 50);
        //画第二条线 3.从上一个lineTo的400,50到这个lineTo的50,400坐标划一条线
        context.lineTo(50, 100);
        //从当前点(50,400)到起始点(beginPath后moveTo的50,50)划一条线
        context.closePath();
        //画笔颜色
        context.strokeStyle = "#ccc";
        //线的宽度
        context.lineWidth = "5";
        //渲染,即开始绘画
        context.stroke();
        /*该例子中closePath()相当于lineto(50,50);故这条线是50,50到100,100两点的直线*/
        context.lineTo(100, 100);
        context.stroke();

        /*清空路径列表,重新创建一个新的路径*/
        context.beginPath();
        /*当前不存在子路径,故此方法作用与moveTo(200,200)相同*/
        context.lineTo(200, 200);
        /*将200,200这个子路径终点连接到250,250*/
        context.lineTo(250, 250);
        /*将连接的直线绘制出来*/
        context.stroke();
    }
</script>
<canvas id="canvas" height="300" width="500" style="background-color: #ff0;"></canvas>
</body>
上述代码示例效果图

绘制贝塞尔曲线 <small>bezierCurve API参考</small>


  • 参数
    • cp1x:第一个控制点的 x 轴坐标;
    • cp1y:第一个控制点的 y 轴坐标;
    • cp2x:第二个控制点的 x 轴坐标;
    • cp2y:第二个控制点的 y 轴坐标;
    • x:结束点的 x 轴坐标;
    • y:结束点的 y 轴坐标。
  • 重点:绘制beziercurve重点在于掌握cp1和cp2两个点的控制;曲线的起点为画笔的起点,曲线的终点为参数x,y的坐标,曲线的弯曲度由cp1和cp2两个点位置的牵引力决定。
<body>
    <!-- beziercurve demo -->
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        ctx.strokeStyle = "#ddd";
        ctx.lineWidth = 1;

        //画横线,y轴每隔10个像素画一条横线
        for (var i = 0.5; i < 500; i++) {
            //创建路径,如果没有此方法,循环会导致里面的线会重复渲染。可比较"画横线"与"画竖线"的区别。
            //ctx.beginPath();
            ctx.moveTo(0, i * 10);
            ctx.lineTo(500, i * 10);
            //若该渲染方法写在for循环之外,即使没有beginPath()方法,也不会重复渲染。
            ctx.stroke();
        }

        //画竖线,x轴每隔10个像素画一条竖线
        for (var i = 0.5; i < 500; i++) {
            ctx.beginPath();
            ctx.moveTo(i * 10, 0);
            ctx.lineTo(i * 10, 500);
            ctx.stroke();
        }
        // beziercurve begin
        ctx.beginPath();
        ctx.moveTo(100, 100);
        ctx.bezierCurveTo(100, 400, 400, 400, 400, 100);
        //如果图形已经是封闭的或者只有一个点,那么此方法不会做任何操作。否则该方法会尝试从当前点到起始点绘制一条直线。
        // ctx.closePath();
        ctx.strokeStyle = "#f00";
        ctx.stroke();
        // beziercurve end

        /*下方代码更好的说明bezierCurverTo六个参数的作用*/

        //第一个控制点,曲线的牵引方向点;bezierCurveTo第一个x,y
        drawArc(ctx, 100, 400, 5, 0, 2 * Math.PI, "#f00", "控制点1");
        //第二个控制点,曲线的牵引方向点;bezierCurveTo第二个x,y
        drawArc(ctx, 400, 400, 5, 0, 2 * Math.PI, "#f00", "控制点2");
        //开始点,moveTo的点
        drawArc(ctx, 100, 100, 5, 0, 2 * Math.PI, "#00f", "起始点");
        //结束点,相当于lineTo的点;bezierCurveTo第三个x,y
        drawArc(ctx, 400, 100, 5, 0, 2 * Math.PI, "#00f", "结束点");

    }

    // 绘制一个圆点及旁注
    function drawArc(ctx, x, y, r, startAngle, endAngle, color, txt) {
        ctx.beginPath();
        ctx.arc(x, y, r, startAngle, endAngle);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.strokeStyle = color;
        // 绘制字符
        ctx.strokeText(txt, x + r + 5, y);
    }
    </script>
    <canvas id="canvas" width="500" height="500"></canvas>
</body>

效果图如下:


贝塞尔曲线示例图

Canvas的渐变


渐变过程中的颜色均使用渐变对象.addColorStop(offset,color)方法设置,多少种颜色渐变就添加多少次此方法。

  • 线性渐变LinearGradient

该渐变对象只定义渐变的起始与终点位置及渐变的角度,在该两点位置之间实现对应角度的渐变。
渐变的有效范围:在canvas范围内根据两点位置及角度决定了渐变范围。<small>关于渐变色的角度:渐变色位于两点构成直线的垂直线范围内</small>

1. 创建LinearGradient对象
  `var lgradient = ctx.createLinearGradient(xstart,ystart,xend,yend)`
2. 添加渐变颜色`lgradient.addColorStop(offset,color)`<small>多少种颜色渐变就添加多少次此方法</small>
3. 将图形绘制(`ctx.fillRect等`)在渐变对象有效的canvas范围内(非渐变对象的参数范围)
<!--LinearGradient & 角度弧度转化 & 根据角度、圆心、半径获取旋转后的目标坐标 DEMO-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        // 创建一个线性渐变对象,并指定起始x,y坐标及结束x,y坐标
        var gradient = ctx.createLinearGradient(0, 0, 100, 50);

        // 渐变色添加 begin;真正的渐变为gradient范围内的50%到80%之间,从白色渐变到绿色
        // offset=0.8,表示green的正色在gradient范围的80%处(从左往右);该offset后的范围均为green正色
        gradient.addColorStop(0.8, "green");
        // offset=0.5,表示white的正色在gradient范围的50%处(从左往右);该offset前的范围均为white正色
        gradient.addColorStop(0.5, "white");
        // 渐变色添加 end

        ctx.fillStyle = gradient;
        // 此处300已超出LinearGradient对象的范围及角度产生的渐变范围,故超出有效渐变范围会根据addColorStop设置的偏移和颜色决定,此处显示green色。
        ctx.fillRect(10, 10, 300, 100);

        // 渐变的范围,超出该范围,都使用接近的正色
        ctx.strokeStyle = "#E3C41B";
        ctx.strokeRect(0, 0, 100, 50);

        // 绘制渐变的方向
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(100, 50);
        ctx.stroke();
        // 求斜线的长度,得出半径
        var r = Math.sqrt(Math.pow(100, 2) + Math.pow(50, 2)) / 2;
        ctx.beginPath();
        ctx.moveTo(50, 25);
        var degree = getDegree(0, 0, 100, 50);
        // 注:*****所有的角度都根据当时坐标点上的坐标轴计算,正数则按顺时针计算*****
        // 1角度=π/180弧度
        //dx 应该是顺时针偏移斜线与x坐标轴的顺时针夹角+90度角算出的x轴坐标
        var dx = 50 + Math.cos(degree * (Math.PI / 180) + Math.PI / 2) * r;
        var dy = 25 + Math.sin(degree * (Math.PI / 180) + Math.PI / 2) * r;
        ctx.lineTo(dx, dy);
        ctx.strokeStyle = "#f00";
        ctx.stroke();

        // 弧度的辅助线
        ctx.beginPath();
        ctx.moveTo(75, 25);
        var cpx1 = cpx2 = 100 - 35;
        var cpy1 = cpy2 = dy - 30;
        ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, dx + (50 - dx) / 2, (dy - 25) / 2 + 25);
        ctx.stroke();
        ctx.strokeStyle = "#000";
        ctx.strokeText("dx,dy参数中的弧度", cpx1, cpy1);

        // 中心点坐标轴
        ctx.beginPath();
        ctx.moveTo(0, 25);
        ctx.lineTo(100, 25);
        ctx.strokeText("x轴", 100, 25);
        ctx.moveTo(100 * 0.5, 0);
        ctx.lineTo(100 * 0.5, 100);
        ctx.strokeText("y轴 offset:0.5", 100 * 0.5, 100);
        // 0.8offset的垂直线
        ctx.moveTo(100 * 0.8, 0);
        ctx.lineTo(100 * 0.8, 80);
        ctx.strokeText("offset:0.8", 100 * 0.8, 80);
        ctx.strokeStyle = "#00a";
        ctx.stroke();

        // 注释
        ctx.strokeText("黄色斜线位置与方向:", 10, 150);
        ctx.strokeText("offset就是颜色正色在该斜线x轴上的偏移量;", 10, 170);
        ctx.strokeText("图中所示offset与黄色斜线相交点:0.8与0.5两个点;", 10, 190);
        ctx.strokeText("两色之间做渐变,根据斜线方向及整个屏幕范围渐变;", 10, 210);
        ctx.strokeText("将图形的style设置为渐变对象,并在渐变屏幕范围", 10, 230);
        ctx.strokeText("内绘制即可得到渐变效果", 10, 250);
    }

    //获取两点之间的直线与起始点坐标x轴顺时针的角度
    function getDegree(sx, sy, dx, dy) {
        // 直角的边长
        var x = Math.abs(sx - dx);
        var y = Math.abs(sy - dy);
        // 根据勾股定理 斜边的平方 = 两直角边的平方之和
        var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        // cos
        var cos = x / z;
        // 获取弧度
        var angle = Math.acos(cos);
        // 转化为角度;1弧度 = 180/π角度
        var degree = 180 / Math.PI * angle;
        console.log(degree);
        return degree;
    }
    </script>
    <canvas id="canvas" height="500" width="500"></canvas>

以上代码效果图如下所示:

LinearGradient示例图(二色渐变)
  • 径向渐变RadialGradient

该渐变对象实现射线式径向渐变,其有效范围为:通过起始圆形两边射线到结束圆形两边并相汇处

1. 创建RadialGradient对象
  `var lgradient = ctx.createRadialGradient(xstart,ystart,rstart,xend,yend,rend)`
2. 添加渐变颜色`lgradient.addColorStop(offset,color)`<small>多少种颜色渐变就添加多少次此方法</small>
3. 将图形绘制(`ctx.fillRect等`)在渐变对象有效范围内(非渐变对象的参数范围)
<!-- RadialGradient Demo-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");
        // 创建射线(径向)渐变对象,六个参数分别是:起始圆形坐标x,y,半径,结束圆形坐标x,y,半径;
        // 通过起始圆形两边射线到结束圆形两边并相汇是结束
        var radiaGradient = ctx.createRadialGradient(50, 50, 50, 200, 200, 400);
        // 设置三个渐变颜色
        radiaGradient.addColorStop(0.3, "#ff0");
        radiaGradient.addColorStop(0.8, "rgb(255,140,0");
        radiaGradient.addColorStop(1, "rgb(255,0,0");
        // 设置填充颜色为渐变对象
        ctx.fillStyle = radiaGradient;
        // 绘制一个矩形
        ctx.fillRect(0, 0, 500, 500);

    }
    </script>
    <canvas id="canvas" height="500" width="500"></canvas>

以上代码效果图如下:

径向渐变示例图(三色渐变)

Canvas的缩放、平移、旋转与ctx的状态保存及还原


  • 缩放:scale(x,y):x,y为缩放因子,会影响缩放对象的所有参数(如宽高、坐标、偏移量,但不影响角度);
  • 平移:translate(x,y):x,y为坐标轴的偏移量,即x表示目标x轴与当前x轴的距离,y表示目标y轴与当前y轴的距离;
  • 旋转:rotate(angle):旋转的弧度;angle = degree * (Math.PI / 180) ;degree为角度。旋转后,ctx的原坐标轴也会随着旋转,旋转后的ctx状态会导致后期绘制的图形其坐标轴也是基于旋转后的坐标轴绘制的;

注意三个方法前后顺序的变化所产生的影响。
1.translate、rotate、scale作用的是整个ctx
2.rotate的旋转的是ctx环境,是根据ctx的当前坐标轴x轴进行的,正弧度表示顺时针,反之逆时针;旋转后起始坐标轴更新到旋转后的坐标轴状态
3.translate的参数为距x轴的距离,距y轴的距离;即起始点x,y的偏移量;同样是根据ctx的当前状态坐标轴进行
translate、rotate、scale会被ctx记录状态,值会被叠加

  • ctx.save():将当前上下文状态保存,以便后期使用;以便某状态被覆盖的时候使用;
  • ctx.restore():还原至最近save的上下文状态。
<!--scale translate rotate & save restore demo-->
<body>
    <script>
    window.onload = function() {
        var canvas = document.getElementById("canvas");
        if (canvas == null) {
            return false;
        }
        var ctx = canvas.getContext("2d");

        ctx.strokeStyle = "#ddd";
        ctx.lineWidth = 1;

        //画横线,y轴每隔10个像素画一条横线
        for (var i = 0.5; i < 500; i++) {
            //创建路径,如果没有此方法,循环会导致里面的线会重复渲染。可比较"画横线"与"画竖线"的区别。
            //ctx.beginPath();
            ctx.moveTo(0, i * 10);
            ctx.lineTo(500, i * 10);
            //若该渲染方法写在for循环之外,即使没有beginPath()方法,也不会重复渲染。
            ctx.stroke();
        }

        //画竖线,x轴每隔10个像素画一条竖线
        for (var i = 0.5; i < 500; i++) {
            ctx.beginPath();
            ctx.moveTo(i * 10, 0);
            ctx.lineTo(i * 10, 500);
            ctx.stroke();
        }

        ctx.fillStyle = "#ff0";
        ctx.fillRect(50, 50, 50, 100);
        // 保存当前状态
        ctx.save();
        ctx.translate(50, 50);
        ctx.fillStyle = "#0f0";
        ctx.fillRect(50, 50, 50, 100);
        // 使用restore后,最近的save状态就会被弹出;
        ctx.restore();
        // 重新保存还原后得到的第一个save状态
        ctx.save();
        for (var i = 0; i < 3; i++) {
            // ctx.scale(0.9, 0.9);
            // x,y分别正向偏移各50像素
            ctx.translate(50, 50);
            ctx.fillStyle = "rgba(255,0,0,0.25)";
            // 以偏移后的50,50坐标点做为起始的0,0坐标点,下方的50,50坐标点以原始0,0计算则为100,100的坐标.
            ctx.fillRect(50, 50, 100, 20);
        }
        // 恢复到save保存的状态
        ctx.restore();
        ctx.translate(300, 300);
        // ctx会记录绘制的状态,循环会导致在记录的状态下不断的scale,不断的translate。
        for (var i = 0; i < 50; i++) {
            // 先缩放,后平移,会导致平移的50,50偏移量随缩放因子改变,及x,y实际偏移量分别为45,45。
            // 即使先平移,后缩放,因循环也会导致平移的参数随缩放因子发生变化。
            // 说明ctx的状态被实时覆盖保存,除非通过save()存档副本
            ctx.scale(0.9, 0.9);
            ctx.translate(10, 10);
            // 顺时针30度角旋转
            ctx.rotate(30 * Math.PI / 180);
            ctx.fillStyle = "rgba(0,0,255,0.25)";
            // scale的缩放因子为0.9,所以实际坐标为偏移后的x坐标+45,偏移后的y坐标+0;宽高为90,18。
            ctx.fillRect(50, 0, 100, 20);
        }
    }
    </script>
    <canvas id="canvas" width="500" height="500"></canvas>
</body>

以上代码效果图如下:

缩放、平移、旋转及状态保存还原的示例
/* translate rotate 重要知识点 */
/* 重点:
 * 1.translate、rotate、scale作用的是整个ctx
 * 2.rotate的旋转的是ctx环境,是根据ctx的当前坐标轴x轴进行的,正弧度表示顺时针,反之逆时针;旋转后起始坐标轴更新到旋转后的坐标轴状态
 * 3.translate的参数为距x轴的距离,距y轴的距离;即起始点x,y的偏移量;同样是根据ctx的当前状态坐标轴进行
 * translate、rotate、scale会被ctx记录状态,值会被叠加
 * */
var secCanvas = document.getElementById("secCanvas");
secCanvas.style.backgroundColor = "#ccc";
ctx = secCanvas.getContext("2d");
for(var i = 0; i < 2; i++) {
    ctx.beginPath();
    //标识ctx的起始点 beigin (蓝色原点)
    ctx.arc(0, 0, 5, 0, 2 * Math.PI, false);
    ctx.fill();
    //标识ctx的起始点 end
    ctx.fillStyle = "royalblue";
    //scale不影响角度,只影响宽高、坐标、偏移量
    //ctx.scale(0.9,0.9);
    var angle = 45 * (Math.PI / 180);
    //基于起点坐标轴x轴顺时针旋转45度角
    //rotate后,ctx的整个坐标轴就旋转了45度角(图中的红线为旋转后的x轴)
    ctx.rotate(angle);/*因为ctx会更新状态,所以会作用到第二次循环*/
    ctx.save(); /*保存旋转后的状态,以便下方划坐标轴用*/

    //划旋转angle弧度后的线条 beigin
    ctx.beginPath();
    ctx.moveTo(0, 0);
    //ctx已经旋转,这里获取旋转角度后对应100半径的坐标点,角度只需0即可
    var dx = Math.cos(0) * 500;
    var dy = Math.sin(0) * 500;
    ctx.lineTo(dx, dy);
    ctx.strokeStyle = "#FF0000";
    ctx.stroke();
    ctx.font = "14px Consolas";
    ctx.strokeStyle = "#228B22";
    ctx.strokeText("第"+(i+1)+"个循环旋转45度后的x坐标轴", dx, dy)
        //划旋转angle弧度后的线条 end

    //标识未translate(50,0)前的rect起始点(红色点)
    ctx.strokeText("第"+(i+1)+"个循环旋转45度后的100,0坐标点", 100, 0)
    ctx.beginPath();
    ctx.arc(100, 0, 5, 0, 2 * Math.PI, false);
    ctx.fillStyle = "#FF0000";
    ctx.fill();
    //基于起点坐标轴x,y轴的偏移距离
    ctx.translate(50, 0);
    ctx.fillRect(100, 0, 100, 50);

    //标识translate(50,0)后的rect起始点(黄色点)
    ctx.strokeText("第"+(i+1)+"个循环旋转45度且偏移50,0后的100,0坐标点", 100, 20)
    ctx.beginPath();
    ctx.arc(100, 0, 5, 0, 2 * Math.PI, false);
    ctx.fillStyle = "#ff0";
    ctx.fill();

    //旋转45度后的坐标轴
    ctx.restore(); /*还原到未translate前*/
    ctx.beginPath();
    //划第一个100,0坐标点的y轴
    //移动到第一个100,0坐标点(红色)
    ctx.moveTo(100, -100);
    ctx.lineTo(100, 100);
    ctx.stroke();
    ctx.font = "14px Arial";
    ctx.strokeText("第"+(i+1)+"个循环旋转45度后的y坐标轴", 110, -100);

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

推荐阅读更多精彩内容