好玩的字符畫

好玩的字符畫播放器 CharPlayer

1. 說明

該項目是將圖片及視頻以字符畫的方式呈現(xiàn)
是抖音比較火爆的特效
基于個人愛好開發(fā)分享給愛折騰的小伙伴們

1.1 技術(shù)說明

引用庫 庫說明 引用說明 備注
vue 虛擬DOM框架 加快dom渲染及內(nèi)部變量間聯(lián)動控制 核心代碼
js-url url解析 url參數(shù)解析使用
stats.js 性能監(jiān)視器 監(jiān)視運行時(幀率/幀耗時/內(nèi)存)情況
hls.js 直播流解析 對m3u8直播流的解析支持
flv.js flv解碼 對flv文件及直播流的解析支持
dot js模板引擎 利用模板加快數(shù)據(jù)幀數(shù)據(jù)生成
libgif GIF解析器和播放器 對gif動畫效果的解析支持
inferno 虛擬DOM框架 利用該框架的高性能差異化渲染 供渲染方法五使用
inferno-create-element inferno擴展 方便inferno的節(jié)點創(chuàng)建

1.2 github開源地址

https://github.com/Febby315/code_wallpaper/tree/master/TXTplayer/v3

1.3 預(yù)覽地址

https://g.febby315.top/TXTplayer/v3/index.html

1.2.1 默認效果預(yù)覽

默認特效.jpg

1.2.2 全特效預(yù)覽

特效全開.jpg

1.4 使用說明

1.4.1 URL參數(shù)說明

參數(shù) 默認值 說明 備注
src video/v.mp4 圖片/視頻uri地址 uri編碼后的字符串
showStats 顯示(幀率秒紧、耗時琴昆、內(nèi)存)性能信息
enableColor 啟用顏色 該特效會嚴重影響性能
enableReverse 反轉(zhuǎn)前景與背景色 目前不美觀
className 啟用內(nèi)置的特效 目前僅支持(shadow快毛、reverse)
style 額外的樣式 經(jīng)過JSON.stringify&的對象字符串

注意: src參數(shù)不支持跨域資源但支持flv刽虹、m3u8直播鏈接
src和style參數(shù)都需要編碼為uri參數(shù)

1.4.2 示例地址

  // 外部圖片源(需要經(jīng)過uri編碼)
  var imgSrc = decodeURIComponent('https://i.loli.net/2019/09/02/yOHcCG7XlFVv4M5.png');
  // 自定義樣式(先經(jīng)過JSON字符序列化再經(jīng)過uri編碼)
  var style = decodeURIComponent(JSON.stringify({ transform: 'scale(-0.8)' }));
  // 陰影&性能信息
  var url = `https://g.febby315.top/TXTplayer/v3/?className=shadow&showStats=1`;
  // 彩色&外鏈圖片:
  var url = `https://g.febby315.top/TXTplayer/v3/?enableColor=1&src=${imgSrc}`;
  // 反轉(zhuǎn)色彩&自定義樣式
  var url = `https://g.febby315.top/TXTplayer/v3/?enableReverse=1&style=${style}`;

1.4.3 支持格式

  • 圖片: (.jpg|.jpeg|.png|.gif)
  • 視頻: (.mp4|.ogg|.webp|.flv)
  • 直播鏈接: (.flv|.m3u8)

2. 完整代碼

2.1. index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>字符畫 v3</title>
    <link rel="stylesheet" href="css/style.css">
    <!-- CDN http://www.jsdelivr.com/ -->
    <script src="http://cdn.jsdelivr.net/npm/js-url@2.3.0/url.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/hls.js@0.13.0/dist/hls.light.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/stats.js@0.17.0/build/stats.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/flv.js@1.5.0/dist/flv.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/dot@1.1.3/doT.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/libgif@0.0.3/libgif.min.js"></script>
    <!-- inferno -->
    <script src="http://cdn.jsdelivr.net/npm/inferno@7.3.3/dist/inferno.min.js"></script>
    <script src="http://cdn.jsdelivr.net/npm/inferno-create-element@7.3.3/dist/inferno-create-element.min.js"></script>
</head>
<body onload="javascript: onload();">
    <div id="app" ref="app">
        <div class="box">
            <!-- @canplay="loadedmetadata($event)" -->
            <!-- preload autoplay controls  -->
            <!-- playsinline webkit-playsinline x5-playsinline  -->
            <!-- x5-video-player-type="h5" x5-video-player-fullscreen="ture" -->
            <video ref="video" class="video" @loadedmetadata="loadedmetadata($event)" @play="play($event)" @pause="pause($event)" @ended="pause($event)" autoplay></video>
            <img ref="image" class="image" @load="imgLoaded($event)" />
            <canvas ref="canvas" class="canvas"></canvas>
        </div>
        <div id="view" ref="view" v-html="content" :class="viewClass" :style="viewStyle"></div>
        <div id="tool" ref="tool">
            <input id="file" ref="file" type="file" @change="fileChange($event)" hidden>
            <button type="button" @click="$refs.file.click();">圖片/視頻</button>
            <button type="button" @click="videoPlay($event)">播放/暫停</button>
        </div>
    </div>
    <script src="js/script.js"></script>
</body>
</html>

2.2. css/style.css

:root{
    --pic-view-bg: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBvbHlnb24gcG9pbnRzPSIwLDAgMTAsMCAxMCwxMCAyMCwxMCAyMCwyMCAxMCwyMCAxMCwxMCAwLDEwIiBzdHlsZT0iZmlsbDpyZ2JhKDEzOCwxMzgsMTM4LDAuMyk7c3Ryb2tlLXdpZHRoOjAiLz48L3N2Zz4=");
}
::-webkit-scrollbar{ display: none; }
/*  */
html{ font-family: monospace; background: #000 var(--pic-view-bg) fixed; }
html,body,#app{ margin: 0px; padding: 0px; width: 100%; height: 100%; overflow: hidden; }
#app{ display: flex; align-items: center; justify-content: center; background-color: rgba(0,0,0,0.25); color: #fff; }
.box{ display: none; opacity: 0.5; }
.video{ position: absolute; left: 0px; top: 0px; max-width: 6vw; max-height: 12vh; }
.image{ position: absolute; left: 0px; bottom: 0px; max-width: 6vw; max-height: 12vh; }
.canvas{ position: fixed; right: 0px; top: 0px; }
/* position: fixed; */
#view{
    /* transform: scale(0.8); width: 100%; */
    bottom: 0px; overflow: hidden; z-index: 2;
    font-family: monospace; font-size: 12px; line-height: 1em; letter-spacing:0px; word-spacing:0px; text-align: center;
}
#tool{ position: fixed; bottom: 0px; right: 0px; padding: 10px; text-align: right; z-index: 10; }
.stats{ position: fixed; top: 0px; z-index: 100; }
/* 紅色陰影特效 */
.shadow{ text-shadow: 2px -1px 1px #f00a; }
/* 顏色反轉(zhuǎn) */
.reverse{ background: #fff; color: #000; }

2.3. js/script.html

const getImageBlob = function(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('get', url, true);
    xhr.responseType = 'blob';
    xhr.onload = function() {
        if(this.status === 200 && callback instanceof Function) callback(URL.createObjectURL(this.response));
    };
    xhr.send();
}

// 頁面body加載完成
function onload(){
    const UA = navigator.userAgent;
    const isAndroid = UA.indexOf('Android') > -1 || UA.indexOf('Adr') > -1; //android終端
    const space = isAndroid? '&nbsp;' : '&ensp;';
    // 利用vue虛擬DOM技術(shù)加速DOM節(jié)點數(shù)據(jù)渲染
    var v$app = window.v$app = new Vue({
        el: "#app",
        data: {
            src: "",
            flvsrc: "http://aliyun-flv.yy.com/live/15013_xv_22490906_22490906_0_0_0-15013_xa_22490906_22490906_0_0_0-96597708953498332-96597708953498333-2-2748477-33.flv?codec=orig&secret=bec0e1c80fad166895855545ff4efc89&t=1562310185&appid=15013",
            m3u8src: "http://ivi.bupt.edu.cn/hls/cctv10.m3u8",
            content: null, // 視圖html內(nèi)容
            timer: null, // 定時器索引
            range: document.createRange(), // 用于通過TagString創(chuàng)建虛擬dom(DocumentFragment)節(jié)點
            stats: new Stats(), // 性能監(jiān)視器:含fps讨越、耗時ms盈厘、內(nèi)存分配
            showStats: !!url("?showStats"), // 顯示統(tǒng)計信息
            enableColor: !!url("?enableColor"), // 啟用輸出色彩
            enableReverse: !!url("?enableReverse"), // 啟用色彩反轉(zhuǎn)
            // 拉伸/自適應(yīng)
            fps: 30, // fps(流暢度)
            fontSize: 12, // 視圖容器字體大小
            chars: [space, '.', ':', ';', '!', 'i', 'c', 'e', 'm', '@'], // 映射字符集;
            styleTemplate: doT.template('color: rgb({{=it.R}},{{=it.G}},{{=it.B}});'), // 彩色字符style模板
            spanTemplate: doT.template('<span style="color:rgb({{=it.R}},{{=it.G}},{{=it.B}});">{{=it.T}}</span>'), // 彩色字符模板
            sw: document.body.offsetWidth, sh: document.body.offsetHeight, // 存儲屏幕寬高(含初始化)
            sourceScale: 1, // 默認素材寬高比
            currRowTempFn: null, // 行模板
            currFrameTempFn: null, // 幀模板
        },
        // 動態(tài)計算
        computed:{
            // 配置灰度字符映射表
            charMap: function() {
                var chars = !this.enableReverse ? this.chars : this.chars.reverse();
                var len = 256, step = ~~(len/(chars.length-1)); // 映射步長=最大字符長度/映射字符長度
                return Array.apply(!0, Array(len)).map(function(v,i,c){
                    return chars[~~(i / step)];
                });
            },
            // 屏幕寬高比
            screenScale: function() {
                return this.sw / this.sh;
            },
            // 屏幕允許最大行數(shù)
            maxRow: function() {
                return ~~(this.sh / this.fontSize);
            },
            // 屏幕允許最大列數(shù)
            maxCol: function() {
                var fontWidth = this.fontSize / 2;
                return ~~(this.sw / fontWidth);
            },
            // 畫面幀間隔時間ms
            fpsStep: function() {
                return 1000 / this.fps;
            },
            viewClass: function() {
                var className = url("?className");
                if(!Array.isArray(className)) className = [className];
                className.push({
                    reverse: url("?enableReverse") // 反轉(zhuǎn)色彩
                });
                return className;
            },
            viewStyle: function() {
                var style = url("?style");
                return style ? JSON.parse(style) : undefined;
            },
        },
        mounted: function() {
            this.$nextTick(function() {
                this.src = url("?src") || "video/v.mp4";
                this.initStats(); // 初始化統(tǒng)計工具
                window.onresize = this.resetToCharsConfig; // 窗口大小改變
            });
        },
        // 數(shù)據(jù)監(jiān)聽
        watch: {
            src: function(nv, ov) {
                var video = this.$refs.video, canvas = this.$refs.canvas;
                this.timer ? clearInterval(this.timer) : null; // 移除定時器
                var ctx = canvas.getContext('2d');
                ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除畫布
                var ext = url("fileext", nv);
                switch(String(ext).toLowerCase()) {
                    case "flv": this.loadFlv(nv, ext); break;
                    case "m3u8": this.loadHls(nv, ext); break;
                    case "jpg": this.loadImage(nv, ext); break;
                    case "png": this.loadImage(nv, ext); break;
                    case "gif": this.loadImage(nv, ext); break;
                    default: video.src = nv; break;
                }
                this.$nextTick(function() {
                    video.load();
                });
            },
            enableColor: function(nv, ov) {
                this.resetToCharsConfig();
            }
        },
        methods: {
            // 加載Flv鏈接地址
            loadFlv: function(src, callback) {
                var video = this.$refs.video;
                if(flvjs.isSupported()) {
                    var flvPlayer = flvjs.createPlayer({ type: 'flv', url: src });
                    flvPlayer.attachMediaElement(video);
                    flvPlayer.load();
                    video.load();
                    // flvPlayer.play();
                    if(callback instanceof Function) callback(flvPlayer);
                }
            },
            // 加載Hls鏈接地址(m3u8)
            loadHls: function(src, callback) {
                var video = this.$refs.video;
                if(Hls.isSupported()) {
                    var hls = new Hls();
                    hls.loadSource(src);
                    hls.attachMedia(video);
                    video.load();
                    if(callback instanceof Function) callback(hls);
                }
            },
            // 加載靜態(tài)圖片鏈接地址
            loadImage: function(src, callback) {
                var image = this.$refs.image;
                getImageBlob(src, function(url) {
                    image.src = url;
                });
            },
            // 渲染幀數(shù)據(jù)
            renderFrame: function(frameData) {
                var enableColor = this.enableColor, spanTemplate = this.spanTemplate;
                return frameData.map(function(rowData) {
                    return rowData.map(function(v) {
                        return enableColor ? spanTemplate(v) : v.T;
                    }).join('');
                }).join('<br/>\n');
            },
            // 實時生成行模板
            rowTempFn: function(rowData) {
                var canvas = this.$refs.canvas, templates = [];
                if(this.enableColor) {
                    for(var i = 0; i < canvas.width; i += 1) {
                        templates.push('<span style="color:rgb({{=it['+i+'].R}},{{=it['+i+'].G}},{{=it['+i+'].B}});">{{=it['+i+'].T}}</span>');
                    }
                } else {
                    for(var i = 0; i < canvas.width; i += 1) {
                        templates.push('{{=it['+i+'].T}}');
                    }
                }
                return doT.template(templates.join(''));
            },
            // 實時生成幀模板
            frameTempFn: function() {
                var canvas = this.$refs.canvas, templates = [];
                if(this.enableColor) {
                    for(var i = 0; i < canvas.height; i += 1) {
                        for(var j = 0; j < canvas.width; j += 1) {
                            templates.push('<span style="color:rgb({{=it['+i+']['+j+'].R}},{{=it['+i+']['+j+'].G}},{{=it['+i+']['+j+'].B}});">{{=it['+i+']['+j+'].T}}</span>');
                        }
                        templates.push('<br/>\n');
                    }
                } else {
                    for(var i = 0; i < canvas.height; i += 1) {
                        for(var j = 0; j < canvas.width; j += 1) {
                            templates.push('{{=it['+i+']['+j+'].T}}');
                        }
                        templates.push('<br/>\n');
                    }
                }
                return doT.template(templates.join(''));
            },
            // 重置采集參數(shù)
            resetToCharsConfig: function() {
                var canvas = this.$refs.canvas, app = this.$refs.app;
                // 采集屏幕寬高
                this.sw = app.offsetWidth;
                this.sh = app.offsetHeight;
                // console.log("最大允許 寬:%s 高:%s ", this.maxCol, this.maxRow);
                // console.log("素材比屏幕寬?(%s) 素材寬高比:%s 屏幕寬高比:%s", this.sourceScale>this.screenScale, this.sourceScale, this.screenScale);
                // 拉伸模式
                // canvas.width = this.maxCol;
                // canvas.height = this.maxRow;

                // 自適應(yīng)模式
                if(this.sourceScale > this.screenScale) {
                    canvas.width = this.maxCol;// 寬度自適應(yīng)
                    // 在寬度自適應(yīng)情況下高度/2與寬度保持比例(因字體高度是寬度的2倍, 為保證畫面與素材保持正確比例)
                    canvas.height = this.maxCol / this.sourceScale / 2;
                }else{
                    canvas.height = this.maxRow;// 高度自適應(yīng)
                    // 在高度自適應(yīng)情況下寬度*2與高度保持比例(因字體高度是寬度的2倍, 為保證畫面與素材保持正確比例)
                    canvas.width = this.maxRow * this.sourceScale * 2;
                }

                // console.log("最終canvas寬高", canvas.width, canvas.height);
                this.currRowTempFn = this.rowTempFn(); // 生成行模版
                this.currFrameTempFn = this.frameTempFn(); // 生成幀模版
            },
            // 繪制canvas
            drawCanvas: function(ctx, ele) {
                const canvas = this.$refs.canvas;
                ctx.drawImage(ele, 0, 0, canvas.width, canvas.height); // 繪制圖像
                this.toFrameData(ctx, canvas.width, canvas.height, this.update); // 將畫布圖像數(shù)據(jù)轉(zhuǎn)換為字符畫
            },
            // 圖像轉(zhuǎn)字符畫數(shù)據(jù)
            toFrameData: function(ctx, cw, ch, callback) {
                const canvas = this.$refs.canvas;
                const styleTemplate = this.styleTemplate;
                var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
                var imgDate = image.data ; // 當(dāng)前畫布圖像數(shù)據(jù)
                // 遍歷每個字符畫像素獲取灰度值映射字符追加至字符畫幀數(shù)據(jù)
                var rowArray = [], rowVNodes = [];
                for(var i = 0, idx = 0; i < image.height; i += 1) {
                    var colArray = [], colVNodes = [];
                    for(var j = 0; j < image.width; j += 1, idx += 4) {
                        var p = { R: 0, G: 0, B: 0 };
                        p.R = ~~imgDate[idx], p.G = ~~imgDate[idx+1], p.B = ~~imgDate[idx+2];
                        // 獲取區(qū)域平均灰度及平均RGB色彩值 為提高效率將單像素灰度計算中的除以100提出
                        // https://www.cnblogs.com/zhangjiansheng/p/6925722.html
                        var Gray = (p.R*38 + p.G*75 + p.B*15) >> 7;
                        p.T = this.charMap[Gray]; // 映射灰度字符
                        colArray.push(p); // 行數(shù)據(jù)
                        colVNodes.push(Inferno.createElement('span', { style: styleTemplate(p) }, Inferno.createTextVNode(p.T)));
                    }
                    rowArray.push(colArray); // 幀數(shù)據(jù)
                    rowVNodes.push(Inferno.createElement('div', null, colVNodes));
                };
                var VNode = Inferno.createElement('div', null, rowVNodes);
                if(callback instanceof Function) callback(rowArray, VNode);
            },
            // 更新畫面
            update: function(frameData, frameVNode) {
                var _this = this, view = this.$refs.view;
                // 方法一 行模板渲染(相較方法二兼容更多瀏覽器,不易發(fā)生棧溢出)
                var frame = frameData.map(function(v) {
                    return _this.currRowTempFn(v);
                }).join("<br/>\n");
                // 方法二 幀模板渲染(效率高但兼容差易超出堆棧上限: Maximum call stack size exceeded)
                // var frame = this.currFrameTempFn(frameData);
                // 方法三 字符模板渲染(效率僅次于方法一,兼容性好);
                // var frame = this.renderFrame(frameData);
                // 方法四 fragment預(yù)加載渲染(無法清除舊的innerHtml)
                // view.innerHtml = null;
                // view.appendChild(this.range.createContextualFragment(frame));
                // 方法五 Inferno差異化渲染(當(dāng)前場景效率低)
                // Inferno.render(frameVNode, view);
                this.content = frame; // 渲染畫面
                this.$nextTick(function() {
                    this.stats.update(); // 觸發(fā)性能統(tǒng)計
                });
            },
            // 初始化統(tǒng)計工具
            initStats: function() {
                var tool = this.$refs.tool, statsEle = this.stats.domElement;
                if(this.showStats && tool && statsEle) {
                    statsEle.className = "stats";
                    tool.appendChild(statsEle);
                }
            },

            // vue事件
            // fileChange 文件更改時修改視頻源
            fileChange: function(e) {
                var file = this.$refs.file, image = this.$refs.image;
                if(file.files[0]) {
                    this.src = URL.createObjectURL(file.files[0]);
                    // 兼容圖片
                    var type = file.files[0].type;
                    if(type.split("/")[0] === "image") {
                        image.src = this.src;
                        image.setAttribute("data-type", type);
                    }
                }
            },
            // imgLoaded 圖片加載成功
            imgLoaded: function(e) {
                var image = this.$refs.image;
                this.sourceScale = image.width/image.height;
                this.resetToCharsConfig();
                // 開始渲染
                var _this = this, canvas = this.$refs.canvas;
                var ctx = canvas.getContext('2d');
                this.drawCanvas(ctx, image);
                // gif支持
                if(["image/gif"].indexOf(image.getAttribute("data-type")) !== -1) {
                    var rub = new SuperGif({ gif: image, progressbar_height: 0 });
                    rub.load(function() {
                        var gifCanvas = rub.get_canvas();
                        _this.timer = setInterval(function() {
                            _this.drawCanvas(ctx, gifCanvas);
                        }, _this.fpsStep);
                    });
                }
            },
            // canplay 媒體可播放
            // loadedmetadata 媒體元數(shù)據(jù)加載
            loadedmetadata: function(e) {
                var video = this.$refs.video;
                this.sourceScale = video.videoWidth/video.videoHeight || this.screenScale;
                this.resetToCharsConfig();
            },
            // play 視頻播放事件
            play: function(e) {
                var _this = this, video = this.$refs.video, canvas = this.$refs.canvas;
                var ctx = canvas.getContext('2d');
                this.timer = setInterval(function() {
                    if(!video.paused) {
                        _this.drawCanvas(ctx, video);
                    }
                }, _this.fpsStep);
            },
            // pause 視頻暫停/停止事件
            pause: function(e) {
                clearInterval(this.timer); // 視頻暫退驯洌或結(jié)束停止定時器
                e.type === "ended" ? this.content=null : null; // 結(jié)束播放清除視圖
            },
            // videoPlay 播放按鈕點擊事件
            videoPlay: function(e) {
                var video = this.$refs.video;
                video.paused ? video.play() : video.pause();
            },
        }
    });
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孟岛,一起剝皮案震驚了整個濱河市瓶竭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渠羞,老刑警劉巖斤贰,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異次询,居然都是意外死亡荧恍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門屯吊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來送巡,“玉大人,你說我怎么就攤上這事盒卸∑” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵蔽介,是天一觀的道長摘投。 經(jīng)常有香客問我,道長虹蓄,這世上最難降的妖魔是什么犀呼? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮武花,結(jié)果婚禮上圆凰,老公的妹妹穿的比我還像新娘。我一直安慰自己体箕,他們只是感情好专钉,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布挑童。 她就那樣靜靜地躺著,像睡著了一般跃须。 火紅的嫁衣襯著肌膚如雪站叼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天菇民,我揣著相機與錄音尽楔,去河邊找鬼。 笑死第练,一個胖子當(dāng)著我的面吹牛阔馋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娇掏,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呕寝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了婴梧?” 一聲冷哼從身側(cè)響起下梢,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎塞蹭,沒想到半個月后孽江,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡番电,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年岗屏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钧舌。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡担汤,死狀恐怖涎跨,靈堂內(nèi)的尸體忽然破棺而出洼冻,到底是詐尸還是另有隱情,我是刑警寧澤隅很,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布撞牢,位于F島的核電站,受9級特大地震影響叔营,放射性物質(zhì)發(fā)生泄漏屋彪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一绒尊、第九天 我趴在偏房一處隱蔽的房頂上張望畜挥。 院中可真熱鬧,春花似錦婴谱、人聲如沸蟹但。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽华糖。三九已至麦向,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間客叉,已是汗流浹背诵竭。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留兼搏,地道東北人卵慰。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像佛呻,于是被迫代替她去往敵國和親呵燕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容