如何選中繪制在canvas上的圖形

項(xiàng)目地址https://github.com/zz632893783/canvasShape

預(yù)覽

a.gif

如何選中繪制在canvas上的圖形

安裝依賴模塊

npm install

運(yùn)行項(xiàng)目

npm run dev

問(wèn)題

繪制在 canvas 上的圖形并不像 html 元素一樣是分開的元素,當(dāng)圖形繪制在canvas上之后这嚣,它們已經(jīng)變成了單純的像素垂券,你在 canvas 上點(diǎn)擊它的時(shí)候宾符,并不會(huì)有任何反應(yīng)厂捞,我們需要其做特殊處理解虱,使它像普通的 html 元素一樣

思路

  • 如果是圓形捅儒,描述一個(gè)圓形查刻,我們需要的是圓心的 x,y 坐標(biāo)秧饮,圓的半徑映挂,還有圓的顏色
  • 如果是矩形,描述一個(gè)矩形(這里先不考慮矩形旋轉(zhuǎn))浦楣,我們需要的是矩形的起始點(diǎn)(左上角點(diǎn))的 x袖肥,y 坐標(biāo),以及矩形的長(zhǎng)寬振劳,還有矩形的顏色
  • 如果是正三角形(這里先不考慮正三角形旋轉(zhuǎn))椎组,我們需要的是三角形中心點(diǎn)的 x,y 坐標(biāo)历恐,三角形的邊長(zhǎng)寸癌,還有三角形的顏色
    除此之代专筷,每種圖形還應(yīng)有一個(gè)參數(shù)用以表示上下級(jí)關(guān)系

總而言之描述每種圖形,所需要的參數(shù)都不相同蒸苇,所以可以將每一種圖形都分別聲明一個(gè)類磷蛹,在它們各自的構(gòu)造函數(shù)中,分別設(shè)置描述該種圖形所需要的數(shù)據(jù)

// 圓形
class Circle {
    constructor (x, y, radius, fillStyle, zIndex = 0) {
        this.type = 'circle'
        this.x = x
        this.y = y
        this.radius = radius
        this.fillStyle = fillStyle
        this.zIndex  = zIndex 
    }
}
// 矩形
class Rectangle {
    constructor (x, y, width, height, fillStyle, zIndex = 0) {
        this.type = 'rectangle'
        this.x = x
        this.y = y
        this.width = width
        this.height = height
        this.fillStyle = fillStyle
        this.zIndex  = zIndex 
    }
}
// 三角形
class Triangle {
    constructor (x, y, side, fillStyle, zIndex = 0) {
        this.type = 'triangle'
        this.x = x
        this.y = y
        this.side = side
        this.fillStyle = fillStyle
        this.zIndex  = zIndex 
    }
}

描述每種圖形的數(shù)據(jù)都不相同溪烤,它們繪制在canvas上使用的方式也不相同

  • 圓形使用的是 canvas 的 arc 方法
  • 矩形可以使用 canvas 的 rect 方法味咳,也可以使用 moveTo 和 lineTo 方法
  • 三角形使用 canvas 的 moveTo 和 lineTo 方法

三角形使用 moveTo 和 lineTo 方法的話,需要知道三角形三個(gè)頂點(diǎn)的 x檬嘀,y 坐標(biāo)槽驶,我們?cè)谌切蚊看螌?shí)例化的時(shí)候,自動(dòng)調(diào)用方法計(jì) computePoint 算三個(gè)點(diǎn)的位置鸳兽,需要用到中學(xué)時(shí)候?qū)W到的三角函數(shù)知識(shí)

class Triangle {
    constructor (x, y, side, fillStyle, zIndex = 0) {
        this.type = 'triangle'
        this.x = x
        this.y = y
        this.side = side
        this.fillStyle = fillStyle
        this.zIndex  = zIndex 
        this.computePoint()
    }
    computePoint () {
        this.pointList = []
        this.pointList.push({
            x: this.x,
            y: this.y - this.side / Math.pow(3, 1 / 2)
        })
        this.pointList.push({
            x: this.x + this.side / 2,
            y: this.y + this.side / Math.pow(3, 1 / 2) / 2
        })
        this.pointList.push({
            x: this.x - this.side / 2,
            y: this.y + this.side / Math.pow(3, 1 / 2) / 2
        })
    }
}
如何繪制

我們?cè)诿總€(gè)圖形的原型上聲明一個(gè) draw 方法

如果是圓形的話雳攘,使用 arc 方法

    draw (ctx) {
        ctx.beginPath()
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
        ctx.closePath()
        ctx.fillStyle = this.fillStyle
        ctx.fill()
    }

如果是矩形的話忽舟,使用 rect 方法

    draw (ctx) {
        ctx.beginPath()
        ctx.rect(this.x, this.y, this.width, this.height)
        ctx.closePath()
        ctx.fillStyle = this.fillStyle
        ctx.fill()
    }

如果是三角形的話卧须,使用 moveTo 和 lineTo 方法萄传,三角形實(shí)例化的時(shí)候,已經(jīng)調(diào)用了 computePoint 計(jì)算了三角形三個(gè)頂點(diǎn)的位置衷掷,保存在 this.pointList 中

    draw (ctx) {
        ctx.beginPath()
        ctx.moveTo(this.pointList[0].x, this.pointList[0].y)
        ctx.lineTo(this.pointList[1].x, this.pointList[1].y)
        ctx.lineTo(this.pointList[2].x, this.pointList[2].y)
        ctx.closePath()
        ctx.fillStyle = this.fillStyle
        ctx.fill()
    }
繪制到 canvas 中

聲明三種圖形類之后辱姨,調(diào)用它們并繪制到 canvas 中,首先就是分別實(shí)例化三種圖形棍鳖,保存在 shapeList 中炮叶,window.requestAnimationFrame 不停地刷新碗旅,每次遍歷 shapeList 的時(shí)候渡处,分別調(diào)用每個(gè)實(shí)例的 draw 方法繪制圖形

<template>
    <canvas class="canvasShape" ref="canvasShape" v-bind:width="width" v-bind:height="height"></canvas>
</template>
<script>
import Circle from '@/lib/circle'
import Triangle from '@/lib/triangle'
import Rectangle from '@/lib/rectangle'
export default {
    data: function () {
        return {
            ctx: null,
            width: 800,
            height: 500,
            shapeList: [
                new Circle(100, 100, 50, 'red', 0),
                new Triangle(200, 200, 100, 'green', 1),
                new Rectangle(300, 300, 100, 50, 'blue', 2)
            ]
        }
    },
    methods: {
        init: function () {
            this.ctx = this.$refs.canvasShape.getContext('2d')
        },
        draw: function () {
            this.ctx.clearRect(0, 0, this.width, this.height)
            this.shapeList.forEach(shape => shape.draw(this.ctx))
        },
        animationFrame: function () {
            window.requestAnimationFrame(() => {
                this.draw()
                this.animationFrame()
            })
        }
    },
    mounted: function () {
        this.init()
        this.animationFrame()
    }
}

</script>
<style lang="stylus" scoped>
.canvasShape {
    box-sizing: content-box;
    border: 1px solid;
}
</style>
如何判斷鼠標(biāo)選中了圖形

和繪制圖形一樣,每種圖形判斷是否選中也不一樣祟辟,所以同樣的医瘫,在每種圖形的原型上聲明一個(gè) isHover 方法,判斷是否選中

  • 圓形: 回憶下中學(xué)數(shù)學(xué)旧困,判斷一個(gè)點(diǎn)是否在圓內(nèi)部的方法醇份,點(diǎn)距離圓心的距離 < 圓的半徑,就可認(rèn)為這個(gè)點(diǎn)在圓內(nèi)部
    isHover (x, y) {
        // 勾股定理
        return (Math.pow(x - this.x, 2) + Math.pow(y - this.y, 2)) <= Math.pow(this.radius, 2)
    }
  • 矩形:矩形非常簡(jiǎn)單吼具,矩形左側(cè)的 x 坐標(biāo) < 鼠標(biāo)點(diǎn)擊的 x 坐標(biāo) < 矩形右側(cè)的 x 坐標(biāo)僚纷,并且 矩形上側(cè)的 y 坐標(biāo) < 鼠標(biāo)點(diǎn)擊的 y 坐標(biāo) < 矩形下側(cè)的 y 坐標(biāo),可認(rèn)為這個(gè)點(diǎn)在矩形內(nèi)部
    isHover (x, y) {
        return this.x <= x && this.x + this.width >= x && this.y <= y && this.y + this.height >= y
    }
  • 三角形:這里又得運(yùn)用中學(xué)數(shù)學(xué)的知識(shí)了拗盒,直線方程式是 y = a * x + b怖竭,我們有一個(gè)點(diǎn) (m, n),將 x = m 帶入方程陡蝇,通過(guò)比較 a * m + b 與 n 的大小關(guān)系痊臭,可以判斷 (m, n) 點(diǎn)是在直線的上方還是直線的下方哮肚。類比到我們的代碼中,判斷 D 點(diǎn)是否在三角形內(nèi)部广匙,可以轉(zhuǎn)換為允趟,D 是否在直線AB的下方,并且在直線AC的下方鸦致,并且在直線BC的上方潮剪,而三角形三個(gè)頂點(diǎn)的坐標(biāo)我們?cè)趯?shí)例化三角形的時(shí)候,就已經(jīng)計(jì)算并保存在 pointList 中了


    未標(biāo)題-3.png

    然后又得運(yùn)用到中學(xué)數(shù)學(xué)的知識(shí)了分唾,已知兩個(gè)點(diǎn)鲁纠,如何求這條直線的方程


    微信截圖_20190928184948.png
    我們可以通過(guò)帶入不同的點(diǎn)來(lái)生成這條直線的方程,轉(zhuǎn)換成代碼
    // 傳入不同的點(diǎn)鳍寂,生成這條直線的函數(shù)
    createLineFunctionByPoint (firstPoint, lastPoint) {
        return x => (firstPoint.y - lastPoint.y) / (firstPoint.x - lastPoint.x) * x + firstPoint.y - (firstPoint.y - lastPoint.y) / (firstPoint.x - lastPoint.x) * firstPoint.x
    }

直線 AB 的方程為

createLineFunctionByPoint(pointA, pointB)

判斷鼠標(biāo)點(diǎn)擊的 (mouseX, mouseY) 是否在直線AB的下方改含,則

// 由于我們的 canvas 坐標(biāo)系的 y 軸和數(shù)學(xué)作坐標(biāo)系相反,所以這里是 <= mouseY
createLineFunctionByPoint(pointA, pointB)(mouseX) <= mouseY

我們代碼中的 pointA 即 shapeList[0]迄汛,pointB 即 shapeList[2]
代碼寫成

    isHover (x, y) {
        if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y) {
            console.log('點(diǎn)擊位置在直線AB的下方')
        }
    }

同理我們帶入不同的點(diǎn)捍壤,用以判斷 D 點(diǎn)是否在 AC 下方,D 點(diǎn)是否在 BC 上方

    isHover (x, y) {
        let result = false
        if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[1])(x) <= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[2])(x) >= y) {
            result = true
        }
        return result
    }
點(diǎn)擊 canvas

當(dāng)點(diǎn)擊 canvas 的時(shí)候鞍爱,取得鼠標(biāo)點(diǎn)擊的 x, y坐標(biāo)鹃觉,遍歷 shapeList 中的每個(gè)圖形實(shí)例,調(diào)用每個(gè)實(shí)例的 isHover 方法睹逃,判斷是否選中了圖形盗扇,如果點(diǎn)擊到了圖形的重合區(qū)域,則比較實(shí)例化時(shí)候傳入的 zIndex 參數(shù)沉填,zIndex 大的為點(diǎn)擊的圖形


微信截圖_20190928190936.png
        mousedownFunc: function (event) {
            let x = event.offsetX
            let y = event.offsetY
            let hoverList = []
            // 首先篩選出點(diǎn)擊的圖形
            this.shapeList.forEach(shape => {
                shape.isHover(x, y) && (hoverList.push(shape))
            })
            if (hoverList.length) {
                // 對(duì)選中的圖形做排序疗隶,zIndex最大的那個(gè)圖形即當(dāng)前鼠標(biāo)選擇的圖形
                hoverList.sort((x, y) => y.zIndex - x.zIndex)
                // 將當(dāng)前選中的圖形實(shí)例賦值到 this.activeShape
                this.activeShape = hoverList[0]
                // 將當(dāng)前選中的圖形的 zIndex 設(shè)置為最大
                this.activeShape.zIndex = Math.max(...this.shapeList.map(shape => shape.zIndex)) + 1
                // 將當(dāng)前的 shapeList 排序,確保 zIndex 越大的圖形越后繪制
                this.shapeList.sort((x, y) => x.zIndex - y.zIndex)
            }
        }
移動(dòng)圖形

點(diǎn)擊圖形的時(shí)候翼闹,首先記錄一次點(diǎn)擊位置距離圖形標(biāo)記位置的偏移量
然后移動(dòng)鼠標(biāo)的時(shí)候斑鼻,圖形位置x = 鼠標(biāo)當(dāng)前x - x方向的偏移量,將這兩個(gè)函數(shù)寫在每個(gè)圖形的原型中

    setOffset (x, y) {
        this.offsetX = x - this.x
        this.offsetY = y - this.y
    }
    setPosition (x, y) {
        this.x = x - this.offsetX
        this.y = y - this.offsetY
    }

由于三角形的繪制方法是由 moveTo 和 lineTo 連線構(gòu)成的猎荠,所以移動(dòng)的時(shí)候坚弱,需要重新調(diào)用一次 computePoint 計(jì)算三個(gè)點(diǎn)的位置

    setPosition (x, y) {
        this.x = x - this.offsetX
        this.y = y - this.offsetY
        this.computePoint()
    }

點(diǎn)擊 canvas 的時(shí)候調(diào)用實(shí)例的 setOffset 方法

       mousedownFunc: function (event) {
            let x = event.offsetX
            let y = event.offsetY
            let hoverList = []
            // 首先篩選出點(diǎn)擊的圖形
            this.shapeList.forEach(shape => {
                shape.isHover(x, y) && (hoverList.push(shape))
            })
            if (hoverList.length) {
                // 對(duì)選中的圖形做排序,zIndex最大的那個(gè)圖形即當(dāng)前鼠標(biāo)選擇的圖形
                hoverList.sort((x, y) => y.zIndex - x.zIndex)
                // 將當(dāng)前選中的圖形實(shí)例賦值到 this.activeShape
                this.activeShape = hoverList[0]
                // 設(shè)置該圖形的偏移量
                this.activeShape.setOffset(x, y)
                // 將當(dāng)前選中的圖形的 zIndex 設(shè)置為最大
                this.activeShape.zIndex = Math.max(...this.shapeList.map(shape => shape.zIndex)) + 1
                // 將當(dāng)前的 shapeList 排序关摇,確保 zIndex 越大的圖形越后繪制
                this.shapeList.sort((x, y) => x.zIndex - y.zIndex)
            }
        }

移動(dòng)鼠標(biāo)的時(shí)候荒叶,重新設(shè)置圖形的位置

        mousemoveFunc: function (event) {
            // 如果現(xiàn)在已經(jīng)選中了一個(gè)圖形
            if (this.activeShape) {
                let x = event.offsetX
                let y = event.offsetY
                this.activeShape.setPosition(x, y)
            }
        }
其他圖形

例如要繪制一個(gè)五角星,我們的思路還是一樣的输虱,首選確定描述這個(gè)圖形需要的參數(shù)些楣,傳入構(gòu)造方法中
確定一個(gè)五角星,需要位置坐標(biāo) x,y戈毒,尺寸艰猬,顏色,確定圖形在 canvas 中的上下層級(jí)埋市,還需要 zIindex冠桃,并且由于五角星我們也是使用 moveTo 和 lineTo 方法,所以還需要知道五角星各個(gè)頂點(diǎn)的坐標(biāo)

    constructor (x, y, side, fillStyle, zIndex = 0) {
        this.type = 'triangle'
        this.x = x
        this.y = y
        this.side = side
        this.fillStyle = fillStyle
        this.zIndex = zIndex
        this.offsetX = this.offsetY = 0
        this.computePoint()
    }

根據(jù)五角星的中心店道宅,計(jì)算各個(gè)頂點(diǎn)的坐標(biāo)食听,同樣需要用到一些中學(xué)數(shù)學(xué)知識(shí)

    computePoint () {
        this.pointList = []
        let temp = (this.side * Math.sin(18 / 180 * Math.PI) + this.side) / Math.cos(18 / 180 * Math.PI)
        this.pointList.push({
            x: temp * Math.cos((72 * 0 - 18 - 72) / 180 * Math.PI) + this.x,
            y: temp * Math.sin((72 * 0 - 18 - 72) / 180 * Math.PI) + this.y
        })
        this.pointList.push({
            x: temp * Math.cos((72 * 1 - 18 - 72) / 180 * Math.PI) + this.x,
            y: temp * Math.sin((72 * 1 - 18 - 72) / 180 * Math.PI) + this.y
        })
        this.pointList.push({
            x: temp * Math.cos((72 * 2 - 18 - 72) / 180 * Math.PI) + this.x,
            y: temp * Math.sin((72 * 2 - 18 - 72) / 180 * Math.PI) + this.y
        })
        this.pointList.push({
            x: temp * Math.cos((72 * 3 - 18 - 72) / 180 * Math.PI) + this.x,
            y: temp * Math.sin((72 * 3 - 18 - 72) / 180 * Math.PI) + this.y
        })
        this.pointList.push({
            x: temp * Math.cos((72 * 4 - 18 - 72) / 180 * Math.PI) + this.x,
            y: temp * Math.sin((72 * 4 - 18 - 72) / 180 * Math.PI) + this.y
        })
    }

這里的 pointList[0],pointList[1]污茵,pointList[2]樱报,pointList[3],pointList[4] 分別對(duì)應(yīng)下圖的 ABCDE 點(diǎn)


微信截圖_20190928194113.png

接著是繪制圖形泞当,順序 A → C → E → B → D

    draw (ctx) {
        ctx.beginPath()
        ctx.moveTo(this.pointList[0].x, this.pointList[0].y)
        ctx.lineTo(this.pointList[2].x, this.pointList[2].y)
        ctx.lineTo(this.pointList[4].x, this.pointList[4].y)
        ctx.lineTo(this.pointList[1].x, this.pointList[1].y)
        ctx.lineTo(this.pointList[3].x, this.pointList[3].y)
        ctx.closePath()
        ctx.fillStyle = this.fillStyle
        ctx.fill()
    }

同樣的再接著是判斷是否被選中迹蛤,將五角星拆分為 △AFJ + △BGF + △CHG + △DIH + △EJI + 五邊形FGHIJ

  • 是否在 △AFJ 中可以轉(zhuǎn)化為,是否在直線 AD 下方襟士,并且在直線 AC 下方盗飒,并且在直線 EB 上方
    A 點(diǎn)對(duì)應(yīng) this.pointList[0]
    B 點(diǎn)對(duì)應(yīng) this.pointList[1]
    C 點(diǎn)對(duì)應(yīng) this.pointList[2]
    D 點(diǎn)對(duì)應(yīng) this.pointList[3]
    E 點(diǎn)對(duì)應(yīng) this.pointList[4]

鼠標(biāo)點(diǎn)擊位置(mouseX, mouseY)是否在 AD 下方可轉(zhuǎn)換為

createLineFunctionByPoint(this.pointList[0], this.pointList[3])(mouseX) <= mouseY

同理可知是否在直線 AC 下方為

createLineFunctionByPoint(this.pointList[0], this.pointList[2])(mouseX) <= mouseY

是否在直線 EB 上方為

createLineFunctionByPoint(this.pointList[1], this.pointList[4])(mouseX) >= mouseY

是否在 △AFJ 中表示為

    isHover (x, y) {
        if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[3])(x) <= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[4])(x) >= y) {
            console.log('在三角形中')
        }
    }

同理可得到另外四個(gè)三角形點(diǎn)擊的代碼
中心的五邊形方法也是相同,即是否在直線 IJ 陋桂,直線 JF 逆趣,直線 FG 下方,并且在直線 GH 嗜历,直線 HI 上方宣渗,將點(diǎn) FGHIJ 帶入 createLineFunctionByPoint 即可
最后得到是否點(diǎn)擊了五角星的代碼

     isHover (x, y) {
        let result = false
        if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[3])(x) <= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[4])(x) >= y) {
            result = true
        } else if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) >= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[3])(x) >= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[4])(x) <= y) {
            result = true
        } else if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[3])(x) <= y && this.createLineFunctionByPoint(this.pointList[2], this.pointList[4])(x) >= y) {
            result = true
        } else if (this.createLineFunctionByPoint(this.pointList[2], this.pointList[4])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[3])(x) >= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[3])(x) <= y) {
            result = true
        } else if (this.createLineFunctionByPoint(this.pointList[1], this.pointList[4])(x) <= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[3])(x) >= y && this.createLineFunctionByPoint(this.pointList[2], this.pointList[4])(x) >= y) {
            result = true
        } else if (this.createLineFunctionByPoint(this.pointList[0], this.pointList[3])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[4])(x) <= y && this.createLineFunctionByPoint(this.pointList[0], this.pointList[2])(x) <= y && this.createLineFunctionByPoint(this.pointList[1], this.pointList[3])(x) >= y && this.createLineFunctionByPoint(this.pointList[2], this.pointList[4])(x) >= y) {
            result = true
        }
        return result
    }

設(shè)置偏移量,移動(dòng)函數(shù)與之前的相同

    setOffset (x, y) {
        this.offsetX = x - this.x
        this.offsetY = y - this.y
    }
    setPosition (x, y) {
        this.x = x - this.offsetX
        this.y = y - this.offsetY
        this.computePoint()
    }
擴(kuò)展到其它圖形

例如繪制幾何圖形組成的海豚


微信截圖_20190928210106.png

其實(shí)思路都是一樣的梨州,將海豚拆分為若干個(gè)幾何圖形


微信截圖_20190928210334.png

按照上面的做法痕囱,構(gòu)造方法中定義各個(gè)點(diǎn)的位置信息,判斷是否點(diǎn)擊的時(shí)候?qū)D形拆分為各個(gè)小的幾何圖形就可以了摊唇,具體就不再贅述了咐蝇,請(qǐng)查看 海豚.psd 中的位置信息,與項(xiàng)目代碼即可
再?gòu)?fù)雜的幾何圖形都可用這種方法
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巷查,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抹腿,更是在濱河造成了極大的恐慌岛请,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件警绩,死亡現(xiàn)場(chǎng)離奇詭異崇败,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門后室,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缩膝,“玉大人,你說(shuō)我怎么就攤上這事岸霹〖膊悖” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵贡避,是天一觀的道長(zhǎng)痛黎。 經(jīng)常有香客問(wèn)我,道長(zhǎng)刮吧,這世上最難降的妖魔是什么湖饱? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮杀捻,結(jié)果婚禮上井厌,老公的妹妹穿的比我還像新娘。我一直安慰自己致讥,他們只是感情好旗笔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拄踪,像睡著了一般蝇恶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惶桐,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天撮弧,我揣著相機(jī)與錄音,去河邊找鬼姚糊。 笑死贿衍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的救恨。 我是一名探鬼主播贸辈,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肠槽!你這毒婦竟也來(lái)了擎淤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秸仙,失蹤者是張志新(化名)和其女友劉穎嘴拢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寂纪,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡席吴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年赌结,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孝冒。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柬姚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庄涡,到底是詐尸還是另有隱情量承,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布啼染,位于F島的核電站宴合,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迹鹅。R本人自食惡果不足惜卦洽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斜棚。 院中可真熱鬧阀蒂,春花似錦、人聲如沸弟蚀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)义钉。三九已至昧绣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捶闸,已是汗流浹背夜畴。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留删壮,地道東北人贪绘。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像央碟,于是被迫代替她去往敵國(guó)和親税灌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • canvas元素的基礎(chǔ)知識(shí) 在頁(yè)面上放置一個(gè)canvas元素亿虽,就相當(dāng)于在頁(yè)面上放置了一塊畫布菱涤,可以在其中進(jìn)行圖形的...
    oWSQo閱讀 10,289評(píng)論 0 19
  • 使用canvas 繪制圖形 上一篇 canvas基本用法在學(xué)習(xí)了canvas基本用法 我們開始著手在 canvas...
    閑不住的李先森閱讀 1,034評(píng)論 0 0
  • 前述:Python程序設(shè)計(jì)可以利用多種方法實(shí)現(xiàn)對(duì)圖像和圖像的呈現(xiàn)和處理,在這是利用Python3.x自帶的tkin...
    IIronMan閱讀 9,065評(píng)論 0 8
  • ??HTML5 添加的最受歡迎的功能就是 元素。這個(gè)元素負(fù)責(zé)在頁(yè)面中設(shè)定一個(gè)區(qū)域坯认,然后就可以通過(guò) JavaScri...
    霜天曉閱讀 3,006評(píng)論 0 2
  • 君為臣綱,君不正,臣投他國(guó).國(guó)為民綱牛哺,國(guó)不正陋气,民起攻之.父為子綱,父不慈引润,子奔他鄉(xiāng).子為父望巩趁,子不正,大義滅親.夫...
    不發(fā)材閱讀 297評(píng)論 0 0