canvas 写字

使用 konvas.js 完成 canvas 写字

效果图
bbbfly

思路

  • 使用Konva.js 库 详细文档Konva.js
  • stage 由 米字格 layer 和 字的 layer 构成
  • 整体 字 的构成 由 线段 > 笔划 > 字
  • 每个笔划 保存成 数组 历史记录
  • 使用api 有 Konva.Stage() Konva.Layer() konva.Group() Konva.Line() Node.hide() Node.show() Canvas.toDataURL() .draw()

详细代码

<style>
        #cvs{
            width: 600px;
            height: 600px;
            margin:0 auto;
        }
        .tools{
            padding: 30px;
            width: 600px;
            margin:0 auto;
            display: flex;
            justify-content: space-around;
        }
        .img-list{
            display: flex;
        }
        .img-list img{
            width: 100px;
            height: 100px;
            border:1px solid #ccc;
            margin-right: 10px;
        }
  </style> 

<body>
    <div id='cvs'></div>
    <div class="tools">
        <label for="">
            选取颜色:
            <input type="color" id='color-picker'/>
        </label>
        <button class="clear">清空</button>
        <button class="save">保存</button>
        <button class="prev">上一笔</button>
        <button class='next' >下一笔</button>
    </div>
    <div class="img-list">
        
    </div>
</body>

<script>
    var colorPicker = document.getElementById('color-picker')
    var cvsEl = document.getElementById('cvs').getBoundingClientRect()
    var stage = new Konva.Stage({
        container:'cvs',
        width: cvsEl.width,
        height: cvsEl.height
    })
    var layer = new Konva.Layer() // 存放 米字格
    var fontLayer = new Konva.Layer() // 存放 字 用于导出图片 分离米字格
    var grid = new Konva.Group() // 米字格 
    var font = new Konva.Group() // 字
    var fontHistory = [] // 记录字的每一个笔划 id
    var lineIndex = -1 // 笔划 index 
    // 笔划最大宽度
    var maxWidth = 30 
    var minWidth = 1
    // 笔划速度
    var maxSpeed = 10 
    var minSpeed = 0.1
    // 初始化 上一次笔划宽度 
    var prevWidth = maxWidth
    // 选取颜色
    var color = '#333'
    colorPicker.addEventListener('input',function(e){
        color = e.target.value
    },false)
    // 清空 字
    document.querySelector('.clear').addEventListener('click',function(){
        font.destroyChildren()
        fontLayer.draw()
        lineIndex = -1
        fontHistory.length = 0
    })
    // 保存 字 成图片
    document.querySelector('.save').addEventListener('click',function(){
        if(!fontHistory.length) return 
        var imgList = document.querySelector('.img-list')
        var img = new Image()
        img.src = fontLayer.canvas.toDataURL('png',1)
        imgList.appendChild(img)
    })
    // 上一笔划
    document.querySelector('.prev').addEventListener('click',function(){
        if(!fontHistory.length || lineIndex === -1) return
        font.findOne(`#${fontHistory[lineIndex--]}`).hide()
        fontLayer.draw()
    })
    // 下一笔划
    document.querySelector('.next').addEventListener('click',function(){
        if(!fontHistory.length || lineIndex === fontHistory.length -1) return
        lineIndex ++
        font.findOne(`#${fontHistory[lineIndex]}`).show()
        font.draw()
    })
    // 动态计算笔划宽度 使笔划 有粗细 过渡 顺滑
    var calculateWidth = function(distance,speed){
        var strokeWidth = 1
        if(speed>= maxSpeed){
            strokeWidth = minWidth
        }else if( speed <= minSpeed){
            strokeWidth = maxWidth
        }else{
            strokeWidth = (maxSpeed-speed) / (maxSpeed-minSpeed) * maxWidth
        }
        return  prevWidth*3/4 + strokeWidth / 4
    }
    // 米字格
    grid.add(new Konva.Line({
        points: [0, 0, stage.width(),0, stage.width(),stage.height(), 0, stage.height(),0,0],
        stroke: 'red',
        strokeWidth: 5,
    }),
    new Konva.Line({
        points: [0,stage.height()/2, stage.width(), stage.height()/2],
        stroke:'red',
        strokeWidth:2,
        dash: [2, 2]
    }),
    new Konva.Line({
        points: [0,0, stage.width(), stage.height()],
        stroke:'red',
        strokeWidth:2,
        dash: [2, 2]
    }),
    new Konva.Line({
        points: [stage.width(), 0, 0, stage.height()],
        stroke:'red',
        strokeWidth:2,
        dash: [2, 2]
    }),
    new Konva.Line({
        points: [stage.width()/2, 0 , stage.width()/2, stage.height()],
        stroke:'red',
        strokeWidth:2,
        dash: [2, 2]
    })
    )
    // 鼠标 事件 写字
    stage.on('mousedown',function(){
        var mousePos = stage.getPointerPosition();
        var x = mousePos.x
        var y = mousePos.y
        this.isReady = true
        this.start_x = x
        this.start_y = y
        this.timestamp = Date.now()
        this.i ? null : this.i = 0
        // 初始化 笔划线条
        this.line = new Konva.Group({
            id: 'line'+this.i++ ,
            name:'line'
        })
        // 往字里 添加 笔划
        font.add(this.line)
    })
    stage.on('mousemove',function(){
        if(!this.isReady) return
        var mousePos = stage.getPointerPosition();
        var x = mousePos.x
        var y = mousePos.y
        var distance = Math.sqrt(Math.pow(x-this.start_x,2)+Math.pow(y-this.start_y,2))
        var now = Date.now()
        var speed = distance / ( now -this.timestamp) * 5
        var strokeWidth = calculateWidth(distance,speed)
        // 移动过程线段 
        this.current = new Konva.Line({
            points:[this.start_x,this.start_y,x,y],
            stroke:color,
            strokeWidth,
            lineCap: 'round',
            lineJoin: 'round',
        })
        // 往笔划里添加 线段
        this.line.add(this.current)
        this.current.draw()

        prevWidth = strokeWidth
        this.timestamp = now
        this.start_x = x
        this.start_y = y
    })
    stage.on('mouseup mouseleave',function(){
        if(this.isReady){
            this.line.getChildren().length 
            ? (fontHistory[++lineIndex] = this.line.id(), fontHistory.length = lineIndex+1 )
            : this.line.destroy() ;
            this.isReady = false

        }
    })
    layer.add(grid)
    fontLayer.add(font)
    stage.add(layer,fontLayer)
    layer.draw()
</script>

版权声明:本文为weixin_43978604原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。