vue delaunay实现狗脸变成猫脸变形动画效果代码

代码语言:html

所属分类:动画

代码描述:vue delaunay实现狗脸变成猫脸变形动画效果代码

代码标签: 狗脸 变成 猫脸 变形 动画 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开


<!DOCTYPE html>
<html lang="en" >

<head>

 
<meta charset="UTF-8">

 
 
 
<style>
body
{
 
margin: 0;
 
font-family: Georgia, sans-serif;
 
background: orange;
}
body h1
{
 
margin: 0;
 
padding: 1em 0;
 
text-align: center;
 
background: steelblue;
 
color: lightcyan;
 
text-shadow: 0.2em 0.2em 0 rgba(0, 0, 0, 0.2);
}
body button
{
 
padding: 1em;
 
font: inherit;
}
body footer
{
 
padding: 2em 1em;
 
background: orangered;
 
color: lightgoldenrodyellow;
}
body footer a
{
 
color: gold;
}

#app {
 
text-align: center;
 
padding: .5em 0;
 
background: aliceblue;
}
#app .group {
 
display: inline-block;
 
vertical-align: top;
 
margin: .5em .8em;
}
#app .group button {
 
display: block;
 
margin: auto;
 
margin-bottom: .5em;
}
#app .image-container {
 
position: relative;
 
display: inline-block;
 
background: white;
}
#app .image-container .draw-area {
 
text-align: left;
 
overflow: hidden;
}
#app .image-container input {
 
position: absolute;
 
z-index: -1;
 
opacity: 0;
 
width: 0;
}
#app .image-container input + span {
 
position: relative;
 
display: block;
 
padding: .5em;
 
box-sizing: border-box;
 
background: purple;
 
color: white;
 
cursor: pointer;
}
#app .image-container input:focus + span, #app .image-container input:active + span,
#app .image-container input + span:hover {
 
background: darkviolet;
 
outline: 2px solid violet;
}
#app #morph {
 
position: relative;
 
display: table;
}
#app #morph canvas {
 
background: white;
}
#app #morph canvas + canvas {
 
position: absolute;
 
top: 0;
 
left: 0;
}
#app .image-container, #app #morph {
 
box-shadow: 0 0  0 1px paleturquoise;
}
#app svg {
 
position: absolute;
 
top: 0;
 
left: 0;
}
#app svg .connector {
 
stroke: rgba(30, 144, 255, 0.5);
 
stroke-width: 1;
 
stroke-dasharray: 2;
 
pointer-events: none;
}
#app svg [data-draggable] {
 
stroke: black;
 
fill: rgba(255, 255, 255, 0.2);
 
fill: url(#point-grad);
 
cursor: move;
}
#app svg [data-draggable].selected {
 
fill: rgba(255, 255, 0, 0.5);
}
#app pre {
 
-webkit-box-flex: 1;
         
flex: 1 1 auto;
 
margin: .5em 1em;
 
background: white;
 
color: #888;
 
border: 1px solid gainsboro;
 
overflow: auto;
}
</style>



</head>

<body>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/vue@2.6.1.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/delaunay-fast.1.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/abo-utils.0.3.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/drag-tracker.1.js"></script>


<h1>HyperMorph 3000™</h1>

<svg width="0" height="0" style="position:absolute;">
   
<defs>
       
<radialGradient id="point-grad">
           
<stop offset="80%" stop-color="transparent"/>
           
<stop offset="81%" stop-color="white"/>
       
</radialGradient>
   
</defs>
</svg>

<section id="app">

   
<div id="prepare" class="group">
       
<button @click="clear">Clear</button>

       
<div id="image1" class="image-container">
           
<div class="draw-area" :style="sizer()">
               
<canvas class="img"></canvas>
               
<triangulator :model="state.tri1" :selected-index="state.selectedIndex"
                              @
added="onAdded" @selected="onSelected" @deleted="onDeleted"></triangulator>
           
</div>
           
<label>
               
<input type="file" accept="image/*">
               
<span>Change image</span>
           
</label>
       
</div>
       
<div id="image2" class="image-container">
           
<div class="draw-area" :style="sizer()">
               
<canvas class="img"></canvas>
               
<triangulator :model="state.tri2" :selected-index="state.selectedIndex"
                              @
added="onAdded" @selected="onSelected" @deleted="onDeleted"></triangulator>
           
</div>
           
<label>
               
<input type="file" accept="image/*">
               
<span>Change image</span>
           
</label>
       
</div>
   
</div>

   
<div id="apply" class="group">
       
<button @click="warp">Morph</button>
       
<div id="morph">
           
<canvas id="c1" :width="state.size.w" :height="state.size.h"></canvas>
           
<canvas id="c2" :width="state.size.w" :height="state.size.h"></canvas>
       
</div>
   
</div>

   
<!---->
   
<pre>{{ state | prettyCompact }}</pre>
   
</section>



<!-- SVG UI -->
<script>
   
Vue.component('drag-node', {
        template
: '<circle data-draggable @dragging="onDragging" :cx="absCoord[0]" :cy="absCoord[1]" :r="r" />',
        props
: {
            r
: { default: 16 },
            coord
: Array,
           
//If 'coord' is relative to some other point:
            offsetCenter
: Array,
       
},
        model
: {
            prop
: 'coord',
            event
: 'do_it',
       
},
        computed
: {
            absCoord
() {
               
const point = this.coord,
                      center
= this.offsetCenter,
                      absCoord
= center ? [ point[0] + center[0], point[1] + center[1] ]
                                       
: point;
               
return absCoord;
           
},
       
},
        methods
: {
            onDragging
(e) {
               
const point = e.detail.pos,
                      center
= this.offsetCenter,
                      relCoord
= center ? [ point[0] - center[0], point[1] - center[1] ]
                                       
: point;
               
this.$emit('do_it', relCoord);
           
},
       
},
   
});

   
Vue.component('connector', {
        template
: '<line class="connector" :x1="start[0]" :y1="start[1]" :x2="absEnd[0]" :y2="absEnd[1]" />',
        props
: ['start', 'end', 'endIsRel'],
        computed
: {
            absEnd
() {
               
const start = this.start,
                      end
= this.end,
                      absEnd
= this.endIsRel ? [ start[0] + end[0], start[1] + end[1] ]
                                             
: end;
               
return absEnd;
           
}
       
}
   
});
</script>

 
 
     
<script>
/**
 * Uses Delaunay triangulation to divide a rectangle into triangles.
 */
class Triangulator {
    constructor(size, points) {
        this.size = size;
        this.points = points || [];
    }
   
    getEffectivePoints() {
        const { w, h } = this.size,
              corners = [
                  Triangulator.createPoint([0,0]),
                  Triangulator.createPoint([w,0]),
                  Triangulator.createPoint([0,h]),
                  Triangulator.createPoint([w,h]),
              ];
        return corners.concat(this.points.filter(p => !p.toDelete));
    }

    getTriangles(indexes) {
        const coords = this.getEffectivePoints().map(p => p.coord),
              triangles = Delaunay.triangulate(coords),
              trisList = [];

        //"...it will return you a giant array, arranged in triplets,
        //    representing triangles by indices into the passed array."
        let a, b, c;
        for(let i = 0; i < triangles.length; i += 3) {
            a = triangles[i];
            b = triangles[i+1];
            c = triangles[i+2];
            trisList.push( indexes ? [a, b, c] : [coords[a], coords[b], coords[c]] );
        }
        return trisList;
    }

    getEdges() {
        const drawn = {},
              edges = [];

        function addIfNew(p1, p2) {
            var key = (p1 < p2) ? (p1 + '_' + p2) : (p2 + '_' + p1);
            if(drawn[key]) { return; }
            drawn[key] = true;

            edges.push([p1, p2]);
        }

        this.getTriangles().forEach(t => {
            addIfNew(t[0], t[1]);
            addIfNew(t[1], t[2]);
            addIfNew(t[2], t[0]);
        });
        return edges;
    }

    addPoint(coord) {
        this.points.push(Triangulator.createPoint(coord));
    }
   
    static createPoint(coord) {
        return {
            coord: coord.map(Math.round),
            //toDelete: false,
        }
    }
}


/**
 * Renders an image on a canvas, within a maximum bounding box.
 */
class ImageRenderer {
    constructor(canvas, onImgLoad) {
        this.canvas = canvas;
        const img = this.image = new Image();

        img.addEventListener('load', e => {
            const w = img.naturalWidth,
                  h = img.naturalHeight,
                  aspect = w/h;

            this.info = {
                width: w,
                height: h,
                aspect,
            };
            onImgLoad(this);
        }, false);
    }
   
    setSrc(src) {
        this.image.src = src;
    }
   
    clampSize(maxW, maxH) {
        const info = this.info;
        if(!info) { throw new Error(`No size info yet (${this.image.src})`); }
       
        const w = info.width,
              h = info.height,
              shrinkageW = maxW / w,
              shrinkageH = maxH / h,
              shrinkage  = Math.min(shrinkageW, shrinkageH),
              clamped = (shrinkage < 1) ? [w * shrinkage, h * shrinkage] : [w, h];

        return clamped;
    }
   
    render(canvSize) {
        const canvas = this.canvas;
        if(canvSize) {
            canvas.width  = canvSize[0];
            canvas.height = canvSize[1];
        }

        const w = canvas.width,
              h = canvas.height,
              [imgW, imgH] = this.clampSize(w, h),
              padW = (w - imgW) / 2,
              padH = (h - imgH) / 2;

        const ctx = canvas.getContext('2d');
        ctx.drawImage(this.image, padW, padH, imgW, imgH);
    }
}


/**
 * Draws a warped image on a canvas by comparing a normal and a warped triangulation.
 */
function warpImage(img, triSource, triTarget, canvas, lerpT) {
    const um = ABOUtils.Math,
          uc = ABOUtils.Canvas,
          ug = ABOUtils.Geom;

    function drawTriangle(s1, s2, s3, d1, d2, d3) {
        //TODO: Expand dest ~.5, and source similarly based on area difference..
        //Overlap the destination areas a little
        //to avoid hairline cracks when drawing mulitiple connected triangles.
        const [d1x, d2x, d3x] = [d1, d2, d3], //ug.expandTriangle(d1, d2, d3, .3),
              [s1x, s2x, s3x] = [s1, s2, s3]; //ug.expandTriangle(s1, s2, s3, .3);

        uc.drawImageTriangle(img, ctx,
                             s1x, s2x, s3x,
                             d1x, d2x, d3x, true);
    }

    const { w, h } = triTarget.size,
          ctx = canvas.getContext('2d'),
          tri1 = triSource.getTriangles(true),
          tri2 = triTarget.getTriangles(true),
          co1 = triSource.getEffectivePoints().map(p => p.coord);

    let co2 = triTarget.getEffectivePoints().map(p => p.coord);
    if(lerpT || (lerpT === 0)) {
        co2 = um.lerp(co1, co2, lerpT);
    }

    ctx.clearRect(0,0, w,h);
    tri1.forEach((t1, i) => {
        const corners1 = t1.map(i => co1[i]),
              corners2 = t1.map(i => co2[i]);

        drawTriangle(corners1[0], corners1[1], corners1[2],
                     corners2[0], corners2[1], corners2[2]);
    });
}


(function() {
    "use strict";
    console.clear();

    const um = ABOUtils.Math,
          ud = ABOUtils.DOM,
          [$, $$] = ud.selectors();


    let _loader1, _loader2;
   
    const _srcA = 'data:image/jpeg;base64,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.........完整代码请登录后点击上方下载按钮下载查看

网友评论0