久久精品中文字幕av,av.涩涩涩涩涩涩涩涩,亚洲国产日韩欧美精品成人久久久,成人福利电影免费在线观看,日产国产原创av,日韩av午夜激情,bt天堂中文字幕,超级碰人妻在线视频,欧美老熟妇久久一区二区

AI寫作智能體 自主規(guī)劃任務(wù),支持聯(lián)網(wǎng)查詢和網(wǎng)頁(yè)讀取,多模態(tài)高效創(chuàng)作各類分析報(bào)告、商業(yè)計(jì)劃、營(yíng)銷方案、教學(xué)內(nèi)容等。 廣告
## Chapter 16 # Drawing on Canvas > [](http://eloquentjavascript.net/16_canvas.html#p_ubdp8gf0Gn)Drawing is deception. > > M.C. Escher,?cited by Bruno Ernst in The Magic Mirror of M.C. Escher [](http://eloquentjavascript.net/16_canvas.html#p_rMRN8Q3Ilr)Browsers give us several ways to display graphics. The simplest way is to use styles to position and color regular DOM elements. This can get you quite far, as the game in the?[previous chapter](http://eloquentjavascript.net/15_game.html#game)?showed. By adding partially transparent background images to the nodes, we can make them look exactly the way we want. It is even possible to rotate or skew nodes by using the?`transform`?style. [](http://eloquentjavascript.net/16_canvas.html#p_4MfzfTYnsB)But we’d be using the DOM for something that it wasn’t originally designed for. Some tasks, such as drawing a line between arbitrary points, are extremely awkward to do with regular HTML elements. [](http://eloquentjavascript.net/16_canvas.html#p_ETEHgjW4XZ)There are two alternatives. The first is DOM-based but utilizes?*Scalable Vector Graphics (SVG)*, rather than HTML elements. Think of SVG as a dialect for describing documents that focuses on shapes rather than text. You can embed an SVG document in an HTML document, or you can include it through an`<img>`?tag. [](http://eloquentjavascript.net/16_canvas.html#p_wU9UQEILbC)The second alternative is called a?*canvas*. A canvas is a single DOM element that encapsulates a picture. It provides a programming interface for drawing shapes onto the space taken up by the node. The main difference between a canvas and an SVG picture is that in SVG the original description of the shapes is preserved so that they can be moved or resized at any time. A canvas, on the other hand, converts the shapes to pixels (colored dots on a raster) as soon as they are drawn and does not remember what these pixels represent. The only way to move a shape on a canvas is to clear the canvas (or the part of the canvas around the shape) and redraw it with the shape in a new position. ## [](http://eloquentjavascript.net/16_canvas.html#h_UPzm0CiZhQ)SVG [](http://eloquentjavascript.net/16_canvas.html#p_gCxXcLPC1N)This book will not go into SVG in detail, but I will briefly explain how it works. At the?[end of the chapter](http://eloquentjavascript.net/16_canvas.html#graphics_tradeoffs), I’ll come back to the trade-offs that you must consider when deciding which drawing mechanism is appropriate for a given application. [](http://eloquentjavascript.net/16_canvas.html#p_aF1bihN0fO)This is an HTML document with a simple SVG picture in it: ~~~ <p>Normal HTML here.</p> <svg xmlns="http://www.w3.org/2000/svg"> <circle r="50" cx="50" cy="50" fill="red"/> <rect x="120" y="5" width="90" height="90" stroke="blue" fill="none"/> </svg> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_sJ+IuoXUNA)The?`xmlns`?attribute changes an element (and its children) to a different?*XML namespace*. This namespace, identified by a URL, specifies the dialect that we are currently speaking. The?`<circle>`?and?`<rect>`?tags, which do not exist in HTML, do have a meaning in SVG—they draw shapes using the style and position specified by their attributes. [](http://eloquentjavascript.net/16_canvas.html#p_DcMLpWRkj0)These tags create DOM elements, just like HTML tags. For example, this changes the?`<circle>`?element to be colored cyan instead: ~~~ var circle = document.querySelector("circle"); circle.setAttribute("fill", "cyan"); ~~~ ## [](http://eloquentjavascript.net/16_canvas.html#h_QXPPgw0nn4)The canvas element [](http://eloquentjavascript.net/16_canvas.html#p_81TEgZtPp7)Canvas graphics can be drawn onto a?`<canvas>`?element. You can give such an element?`width`?and?`height`?attributes to determine its size in pixels. [](http://eloquentjavascript.net/16_canvas.html#p_oQeTfkBH6v)A new canvas is empty, meaning it is entirely transparent and thus shows up simply as empty space in the document. [](http://eloquentjavascript.net/16_canvas.html#p_r+b1t0y8Si)The?`<canvas>`?tag is intended to support different styles of drawing. To get access to an actual drawing interface, we first need to create a?*context*, which is an object whose methods provide the drawing interface. There are currently two widely supported drawing styles:?`"2d"`?for two-dimensional graphics and`"webgl"`?for three-dimensional graphics through the OpenGL interface. [](http://eloquentjavascript.net/16_canvas.html#p_JaC7mTUVQ0)This book won’t discuss WebGL. We stick to two dimensions. But if you are interested in three-dimensional graphics, I do encourage you to look into WebGL. It provides a very direct interface to modern graphics hardware and thus allows you to render even complicated scenes efficiently, using JavaScript. [](http://eloquentjavascript.net/16_canvas.html#p_GM4b1ASHzf)A context is created through the?`getContext`?method on the?`<canvas>`element. ~~~ <p>Before canvas.</p> <canvas width="120" height="60"></canvas> <p>After canvas.</p> <script> var canvas = document.querySelector("canvas"); var context = canvas.getContext("2d"); context.fillStyle = "red"; context.fillRect(10, 10, 100, 50); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_fv4PQSHT32)After creating the context object, the example draws a red rectangle 100 pixels wide and 50 pixels high, with its top-left corner at coordinates (10,10). [](http://eloquentjavascript.net/16_canvas.html#p_e1SRsTstnD)Just like in HTML (and SVG), the coordinate system that the canvas uses puts (0,0) at the top-left corner, and the positive y-axis goes down from there. So (10,10) is 10 pixels below and to the right of the top-left corner. ## [](http://eloquentjavascript.net/16_canvas.html#h_pcnyueRl1y)Filling and stroking [](http://eloquentjavascript.net/16_canvas.html#p_UvxB22/zji)In the canvas interface, a shape can be?*filled*, meaning its area is given a certain color or pattern, or it can be?*stroked*, which means a line is drawn along its edge. The same terminology is used by SVG. [](http://eloquentjavascript.net/16_canvas.html#p_jXutP8Kp6D)The?`fillRect`?method fills a rectangle. It takes first the x- and y-coordinates of the rectangle’s top-left corner, then its width, and then its height. A similar method,?`strokeRect`, draws the outline of a rectangle. [](http://eloquentjavascript.net/16_canvas.html#p_a+mohoFrQD)Neither method takes any further parameters. The color of the fill, thickness of the stroke, and so on are not determined by an argument to the method (as you might justly expect) but rather by properties of the context object. [](http://eloquentjavascript.net/16_canvas.html#p_qwtIen1h5e)Setting?`fillStyle`?changes the way shapes are filled. It can be set to a string that specifies a color, and any color understood by CSS can also be used here. [](http://eloquentjavascript.net/16_canvas.html#p_z2EgIJKmOE)The?`strokeStyle`?property works similarly but determines the color used for a stroked line. The width of that line is determined by the?`lineWidth`?property, which may contain any positive number. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.strokeStyle = "blue"; cx.strokeRect(5, 5, 50, 50); cx.lineWidth = 5; cx.strokeRect(135, 5, 50, 50); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_AIygWrCmAA)When no?`width`?or?`height`?attribute is specified, as in the previous example, a canvas element gets a default width of 300 pixels and height of 150 pixels. ## [](http://eloquentjavascript.net/16_canvas.html#h_E+fhFyL32D)Paths [](http://eloquentjavascript.net/16_canvas.html#p_EjhhpUs4B/)A path is a sequence of lines. The 2D canvas interface takes a peculiar approach to describing such a path. It is done entirely through side effects. Paths are not values that can be stored and passed around. Instead, if you want to do something with a path, you make a sequence of method calls to describe its shape. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); for (var y = 10; y < 100; y += 10) { cx.moveTo(10, y); cx.lineTo(90, y); } cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_CWuGebDoSl)This example creates a path with a number of horizontal line segments and then strokes it using the?`stroke`?method. Each segment created with?`lineTo`starts at the path’s?*current*?position. That position is usually the end of the last segment, unless?`moveTo`?was called. In that case, the next segment would start at the position passed to?`moveTo`. [](http://eloquentjavascript.net/16_canvas.html#p_/C3/H/w2FX)When filling a path (using the?`fill`?method), each shape is filled separately. A path can contain multiple shapes—each?`moveTo`?motion starts a new one. But the path needs to be?*closed*?(meaning its start and end are in the same position) before it can be filled. If the path is not already closed, a line is added from its end to its start, and the shape enclosed by the completed path is filled. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); cx.moveTo(50, 10); cx.lineTo(10, 70); cx.lineTo(90, 70); cx.fill(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_9unYw61//h)This example draws a filled triangle. Note that only two of the triangle’s sides are explicitly drawn. The third, from the bottom-right corner back to the top, is implied and won’t be there when you stroke the path. [](http://eloquentjavascript.net/16_canvas.html#p_6TyHPpw704)You could also use the?`closePath`?method to explicitly close a path by adding an actual line segment back to the path’s start. This segment?*is*?drawn when stroking the path. ## [](http://eloquentjavascript.net/16_canvas.html#h_B8g7k6vws+)Curves [](http://eloquentjavascript.net/16_canvas.html#p_xee0Jzlsic)A path may also contain curved lines. These are, unfortunately, a bit more involved to draw than straight lines. [](http://eloquentjavascript.net/16_canvas.html#p_q2PxOrpPav)The?`quadraticCurveTo`?method draws a curve to a given point. To determine the curvature of the line, the method is given a control point as well as a destination point. Imagine this control point as?*attracting*?the line, giving the line its curve. The line won’t go through the control point. Rather, the direction of the line at its start and end points will be such that it aligns with the line from there to the control point. The following example illustrates this: ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); cx.moveTo(10, 90); // control=(60,10) goal=(90,90) cx.quadraticCurveTo(60, 10, 90, 90); cx.lineTo(60, 10); cx.closePath(); cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_OIjzl6Dc9Y)We draw a quadratic curve from the left to the right, with (60,10) as control point, and then draw two line segments going through that control point and back to the start of the line. The result somewhat resembles a?*Star Trek*insignia. You can see the effect of the control point: the lines leaving the lower corners start off in the direction of the control point and then curve toward their target. [](http://eloquentjavascript.net/16_canvas.html#p_Nd10AmdUxD)The?`bezierCurveTo`?method draws a similar kind of curve. Instead of a single control point, this one has two—one for each of the line's endpoints. Here is a similar sketch to illustrate the behavior of such a curve: ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); cx.moveTo(10, 90); // control1=(10,10) control2=(90,10) goal=(50,90) cx.bezierCurveTo(10, 10, 90, 10, 50, 90); cx.lineTo(90, 10); cx.lineTo(10, 10); cx.closePath(); cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_UVt0ID9CaV)The two control points specify the direction at both ends of the curve. The further they are away from their corresponding point, the more the curve will “bulge” in that direction. [](http://eloquentjavascript.net/16_canvas.html#p_tMRY3uv3sU)Such curves can be hard to work with—it’s not always clear how to find the control points that provide the shape you are looking for. Sometimes you can compute them, and sometimes you’ll just have to find a suitable value by trial and error. [](http://eloquentjavascript.net/16_canvas.html#p_A4ndRHX5Et)*Arcs*—fragments of a circle—are easier to reason about. The?`arcTo`?method takes no less than five arguments. The first four arguments act somewhat like the arguments to?`quadraticCurveTo`. The first pair provides a sort of control point, and the second pair gives the line’s destination. The fifth argument provides the radius of the arc. The method will conceptually project a corner—a line going to the control point and then to the destination point—and round the corner’s point so that it forms part of a circle with the given radius. The?`arcTo`method then draws the rounded part, as well as a line from the starting position to the start of the rounded part. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); cx.moveTo(10, 10); // control=(90,10) goal=(90,90) radius=20 cx.arcTo(90, 10, 90, 90, 20); cx.moveTo(10, 10); // control=(90,10) goal=(90,90) radius=80 cx.arcTo(90, 10, 90, 90, 80); cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_+oDzi4kZW+)The?`arcTo`?method won’t draw the line from the end of the rounded part to the goal position, though the word?*to*?in its name would suggest it does. You can follow up with a call to?`lineTo`?with the same goal coordinates to add that part of the line. [](http://eloquentjavascript.net/16_canvas.html#p_amjPveUnsp)To draw a circle, you could use four calls to?`arcTo`?(each turning 90 degrees). But the?`arc`?method provides a simpler way. It takes a pair of coordinates for the arc’s center, a radius, and then a start and end angle. [](http://eloquentjavascript.net/16_canvas.html#p_R0hMJ8VOzu)Those last two parameters make it possible to draw only part of circle. The angles are measured in radians, not degrees. This means a full circle has an angle of 2π, or?`2 * Math.PI`, which is about 6.28\. The angle starts counting at the point to the right of the circle’s center and goes clockwise from there. You can use a start of 0 and an end bigger than 2π (say, 7) to draw a full circle. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.beginPath(); // center=(50,50) radius=40 angle=0 to 7 cx.arc(50, 50, 40, 0, 7); // center=(150,50) radius=40 angle=0 to ?π cx.arc(150, 50, 40, 0, 0.5 * Math.PI); cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_NhI3gukqbY)The resulting picture contains a line from the right of the full circle (first call to`arc`) to the right of the quarter-circle (second call). Like other path-drawing methods, a line drawn with?`arc`?is connected to the previous path segment by default. You’d have to call?`moveTo`?or start a new path if you want to avoid this. ## [](http://eloquentjavascript.net/16_canvas.html#h_9yOdkmATfT)Drawing a pie chart [](http://eloquentjavascript.net/16_canvas.html#p_Rd++tqzkHn)Imagine you’ve just taken a job at EconomiCorp, Inc., and your first assignment is to draw a pie chart of their customer satisfaction survey results. [](http://eloquentjavascript.net/16_canvas.html#p_NOg95U8NVA)The?`results`?variable contains an array of objects that represent the survey responses. ~~~ var results = [ {name: "Satisfied", count: 1043, color: "lightblue"}, {name: "Neutral", count: 563, color: "lightgreen"}, {name: "Unsatisfied", count: 510, color: "pink"}, {name: "No comment", count: 175, color: "silver"} ]; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_P3SJQbINGf)To draw a pie chart, we draw a number of pie slices, each made up of an arc and a pair of lines to the center of that arc. We can compute the angle taken up by each arc by dividing a full circle (2π) by the total number of responses and then multiplying that number (the angle per response) by the number of people who picked a given choice. ~~~ <canvas width="200" height="200"></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var total = results.reduce(function(sum, choice) { return sum + choice.count; }, 0); // Start at the top var currentAngle = -0.5 * Math.PI; results.forEach(function(result) { var sliceAngle = (result.count / total) * 2 * Math.PI; cx.beginPath(); // center=100,100, radius=100 // from current angle, clockwise by slice's angle cx.arc(100, 100, 100, currentAngle, currentAngle + sliceAngle); currentAngle += sliceAngle; cx.lineTo(100, 100); cx.fillStyle = result.color; cx.fill(); }); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_e5+MpAPZp1)But a chart that doesn’t tell us what it means isn’t very helpful. We need a way to draw text to the canvas. ## [](http://eloquentjavascript.net/16_canvas.html#h_wzKMObDin3)Text [](http://eloquentjavascript.net/16_canvas.html#p_/MkzAT+yJW)A 2D canvas drawing context provides the methods?`fillText`?and`strokeText`. The latter can be useful for outlining letters, but usually`fillText`?is what you need. It will fill the given text with the current`fillColor`. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.font = "28px Georgia"; cx.fillStyle = "fuchsia"; cx.fillText("I can draw text, too!", 10, 50); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_n79D894g2i)You can specify the size, style, and font of the text with the?`font`?property. This example just gives a font size and family name. You can add?`italic`?or?`bold`?to the start of the string to select a style. [](http://eloquentjavascript.net/16_canvas.html#p_3LlDU7wt/M)The last two arguments to?`fillText`?(and?`strokeText`) provide the position at which the font is drawn. By default, they indicate the position of the start of the text’s alphabetic baseline, which is the line that letters “stand” on, not counting hanging parts in letters like?*j*?or?*p*. You can change the horizontal position by setting the?`textAlign`?property to?`"end"`?or?`"center"`?and the vertical position by setting?`textBaseline`?to?`"top"`,?`"middle"`, or?`"bottom"`. [](http://eloquentjavascript.net/16_canvas.html#p_/4NicYFaZd)We will come back to our pie chart, and the problem of labeling the slices, in the?[exercises](http://eloquentjavascript.net/16_canvas.html#exercise_pie_chart)?at the end of the chapter. ## [](http://eloquentjavascript.net/16_canvas.html#h_CehxyY/vO5)Images [](http://eloquentjavascript.net/16_canvas.html#p_y7aK+8CMcM)In computer graphics, a distinction is often made between?*vector*?graphics and*bitmap*?graphics. The first is what we have been doing so far in this chapter—specifying a picture by giving a logical description of shapes. Bitmap graphics, on the other hand, don’t specify actual shapes but rather work with pixel data (rasters of colored dots). [](http://eloquentjavascript.net/16_canvas.html#p_qlIKv8QV+7)The?`drawImage`?method allows us to draw pixel data onto a canvas. This pixel data can originate from an?`<img>`?element or from another canvas, and neither has to be visible in the actual document. The following example creates a detached?`<img>`?element and loads an image file into it. But it cannot immediately start drawing from this picture because the browser may not have fetched it yet. To deal with this, we register a?`"load"`?event handler and do the drawing after the image has loaded. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var img = document.createElement("img"); img.src = "img/hat.png"; img.addEventListener("load", function() { for (var x = 10; x < 200; x += 30) cx.drawImage(img, x, 10); }); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_RxRhSVTbGY)By default,?`drawImage`?will draw the image at its original size. You can also give it two additional arguments to dictate a different width and height. [](http://eloquentjavascript.net/16_canvas.html#p_/bLIqvw4R4)When?`drawImage`?is given?*nine*?arguments, it can be used to draw only a fragment of an image. The second through fifth arguments indicate the rectangle (x, y, width, and height) in the source image that should be copied, and the sixth to ninth arguments give the rectangle (on the canvas) into which it should be copied. [](http://eloquentjavascript.net/16_canvas.html#p_xKlo8wmmgJ)This can be used to pack multiple?*sprites*?(image elements) into a single image file and then draw only the part you need. For example, we have this picture containing a game character in multiple poses: ![](https://box.kancloud.cn/2015-10-31_563439aa15f50.png) [](http://eloquentjavascript.net/16_canvas.html#p_u8W4Vqh39a)By alternating which pose we draw, we can show an animation that looks like a walking character. [](http://eloquentjavascript.net/16_canvas.html#p_vwSjUDbC98)To animate the picture on a canvas, the?`clearRect`?method is useful. It resembles?`fillRect`, but instead of coloring the rectangle, it makes it transparent, removing the previously drawn pixels. [](http://eloquentjavascript.net/16_canvas.html#p_VcrdBA3T8z)We know that each?*sprite*, each subpicture, is 24 pixels wide and 30 pixels high. The following code loads the image and then sets up an interval (repeated timer) to draw the next?*frame*: ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var img = document.createElement("img"); img.src = "img/player.png"; var spriteW = 24, spriteH = 30; img.addEventListener("load", function() { var cycle = 0; setInterval(function() { cx.clearRect(0, 0, spriteW, spriteH); cx.drawImage(img, // source rectangle cycle * spriteW, 0, spriteW, spriteH, // destination rectangle 0, 0, spriteW, spriteH); cycle = (cycle + 1) % 8; }, 120); }); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_FwoYTH1Pg7)The?`cycle`?variable tracks our position in the animation. Each frame, it is incremented and then clipped back to the 0 to 7 range by using the remainder operator. This variable is then used to compute the x-coordinate that the sprite for the current pose has in the picture. ## [](http://eloquentjavascript.net/16_canvas.html#h_3BwjEnWhbh)Transformation [](http://eloquentjavascript.net/16_canvas.html#p_b00lbwHiev)But what if we want our character to walk to the left instead of to the right? We could add another set of sprites, of course. But we can also instruct the canvas to draw the picture the other way round. [](http://eloquentjavascript.net/16_canvas.html#p_JnX4LFnuKL)Calling the?`scale`?method will cause anything drawn after it to be scaled. This method takes two parameters, one to set a horizontal scale and one to set a vertical scale. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); cx.scale(3, .5); cx.beginPath(); cx.arc(50, 50, 40, 0, 7); cx.lineWidth = 3; cx.stroke(); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_u+8/LLl96H)Scaling will cause everything about the drawn image, including the line width, to be stretched out or squeezed together as specified. Scaling by a negative amount will flip the picture around. The flipping happens around point (0,0), which means it will also flip the direction of the coordinate system. When a horizontal scaling of -1 is applied, a shape drawn at x position 100 will end up at what used to be position -100. [](http://eloquentjavascript.net/16_canvas.html#p_bZnJ+aZtFS)So to turn a picture around, we can’t simply add?`cx.scale(-1, 1)`?before the call to?`drawImage`?since that would move our picture outside of the canvas, where it won’t be visible. You could adjust the coordinates given to?`drawImage`to compensate for this by drawing the image at x position -50 instead of 0\. Another solution, which doesn’t require the code that does the drawing to know about the scale change, is to adjust the axis around which the scaling happens. [](http://eloquentjavascript.net/16_canvas.html#p_LbiF7s9yt4)There are several other methods besides?`scale`?that influence the coordinate system for a canvas. You can rotate subsequently drawn shapes with the`rotate`?method and move them with the?`translate`?method. The interesting—and confusing—thing is that these transformations?*stack*, meaning that each one happens relative to the previous transformations. [](http://eloquentjavascript.net/16_canvas.html#p_mkNUtuNxcG)So if we translate by 10 horizontal pixels twice, everything will be drawn 20 pixels to the right. If we first move the center of the coordinate system to (50,50) and then rotate by 20 degrees (0.1π in radians), that rotation will happen?*around*?point (50,50). ![](https://box.kancloud.cn/2015-10-31_563439aa23362.svg) [](http://eloquentjavascript.net/16_canvas.html#p_TGBwm05/cn)But if we?*first*?rotate by 20 degrees and?*then*?translate by (50,50), the translation will happen in the rotated coordinate system and thus produce a different orientation. The order in which transformations are applied matters. [](http://eloquentjavascript.net/16_canvas.html#p_9a1O8aEtUA)To flip a picture around the vertical line at a given x position, we can do the following: ~~~ function flipHorizontally(context, around) { context.translate(around, 0); context.scale(-1, 1); context.translate(-around, 0); } ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_qob4Y7lZ5I)We move the y-axis to where we want our mirror to be, apply the mirroring, and finally move the y-axis back to its proper place in the mirrored universe. The following picture explains why this works: ![](https://box.kancloud.cn/2015-10-31_563439aa2dc29.svg) [](http://eloquentjavascript.net/16_canvas.html#p_AGahdKv9Zv)This shows the coordinate systems before and after mirroring across the central line. If we draw a triangle at a positive x position, it would, by default, be in the place where triangle 1 is. A call to?`flipHorizontally`?first does a translation to the right, which gets us to triangle 2\. It then scales, flipping the triangle back to position 3\. This is not where it should be, if it were mirrored in the given line. The second?`translate`?call fixes this—it “cancels” the initial translation and makes triangle 4 appear exactly where it should. [](http://eloquentjavascript.net/16_canvas.html#p_akkrexzSeV)We can now draw a mirrored character at position (100,0) by flipping the world around the character’s vertical center. ~~~ <canvas></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var img = document.createElement("img"); img.src = "img/player.png"; var spriteW = 24, spriteH = 30; img.addEventListener("load", function() { flipHorizontally(cx, 100 + spriteW / 2); cx.drawImage(img, 0, 0, spriteW, spriteH, 100, 0, spriteW, spriteH); }); </script> ~~~ ## [](http://eloquentjavascript.net/16_canvas.html#h_Z+iS7LhRr9)Storing and clearing transformations [](http://eloquentjavascript.net/16_canvas.html#p_Az5iI5GbrT)Transformations stick around. Everything else we draw after drawing that mirrored character would also be mirrored. That might be a problem. [](http://eloquentjavascript.net/16_canvas.html#p_4R4LetOdqr)It is possible to save the current transformation, do some drawing and transforming, and then restore the old transformation. This is usually the proper thing to do for a function that needs to temporarily transform the coordinate system. First, we save whatever transformation the code that called the function was using. Then, the function does its thing (on top of the existing transformation), possibly adding more transformations. And finally, we revert to the transformation that we started with. [](http://eloquentjavascript.net/16_canvas.html#p_65OomGfaeV)The?`save`?and?`restore`?methods on the 2D canvas context perform this kind of transformation management. They conceptually keep a stack of transformation states. When you call?`save`, the current state is pushed onto the stack, and when you call?`restore`, the state on top of the stack is taken off and used as the context’s current transformation. [](http://eloquentjavascript.net/16_canvas.html#p_+U4XOiej4f)The?`branch`?function in the following example illustrates what you can do with a function that changes the transformation and then calls another function (in this case itself), which continues drawing with the given transformation. [](http://eloquentjavascript.net/16_canvas.html#p_2U+6WbJ0Or)This function draws a treelike shape by drawing a line, moving the center of the coordinate system to the end of the line, and calling itself twice—first rotated to the left and then rotated to the right. Every call reduces the length of the branch drawn, and the recursion stops when the length drops below 8. ~~~ <canvas width="600" height="300"></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); function branch(length, angle, scale) { cx.fillRect(0, 0, 1, length); if (length < 8) return; cx.save(); cx.translate(0, length); cx.rotate(-angle); branch(length * scale, angle, scale); cx.rotate(2 * angle); branch(length * scale, angle, scale); cx.restore(); } cx.translate(300, 0); branch(60, 0.5, 0.8); </script> ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_Fr9r9ZmPHu)If the calls to?`save`?and?`restore`?were not there, the second recursive call to`branch`?would end up with the position and rotation created by the first call. It wouldn’t be connected to the current branch but rather to the innermost, rightmost branch drawn by the first call. The resulting shape might also be interesting, but it is definitely not a tree. ## [](http://eloquentjavascript.net/16_canvas.html#h_TOqgrv5vzl)Back to the game [](http://eloquentjavascript.net/16_canvas.html#p_v/iifj4dhd)We now know enough about canvas drawing to start working on a canvas-based display system for the game from the?[previous chapter](http://eloquentjavascript.net/15_game.html#game). The new display will no longer be showing just colored boxes. Instead, we’ll use?`drawImage`?to draw pictures that represent the game’s elements. [](http://eloquentjavascript.net/16_canvas.html#p_dy1myCUsaF)We will define an object type?`CanvasDisplay`, supporting the same interface as`DOMDisplay`?from?[Chapter 15](http://eloquentjavascript.net/15_game.html#domdisplay), namely, the methods?`drawFrame`?and?`clear`. [](http://eloquentjavascript.net/16_canvas.html#p_H/XEh6pFg7)This object keeps a little more information than?`DOMDisplay`. Rather than using the scroll position of its DOM element, it tracks its own viewport, which tells us what part of the level we are currently looking at. It also tracks time and uses that to decide which animation frame to use. And finally, it keeps a`flipPlayer`?property so that even when the player is standing still, it keeps facing the direction it last moved in. ~~~ function CanvasDisplay(parent, level) { this.canvas = document.createElement("canvas"); this.canvas.width = Math.min(600, level.width * scale); this.canvas.height = Math.min(450, level.height * scale); parent.appendChild(this.canvas); this.cx = this.canvas.getContext("2d"); this.level = level; this.animationTime = 0; this.flipPlayer = false; this.viewport = { left: 0, top: 0, width: this.canvas.width / scale, height: this.canvas.height / scale }; this.drawFrame(0); } CanvasDisplay.prototype.clear = function() { this.canvas.parentNode.removeChild(this.canvas); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_blt6VbEw4l)The?`animationTime`?counter is the reason we passed the step size to`drawFrame`?in?[Chapter 15](http://eloquentjavascript.net/15_game.html#domdisplay), even though?`DOMDisplay`?does not use it. Our new`drawFrame`?function uses the counter to track time so that it can switch between animation frames based on the current time. ~~~ CanvasDisplay.prototype.drawFrame = function(step) { this.animationTime += step; this.updateViewport(); this.clearDisplay(); this.drawBackground(); this.drawActors(); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_hwAtbz1fs3)Other than tracking time, the method updates the viewport for the current player position, fills the whole canvas with a background color, and draws the background and actors onto that. Note that this is different from the approach in?[Chapter 15](http://eloquentjavascript.net/15_game.html#domdisplay), where we drew the background once and scrolled the wrapping DOM element to move it. [](http://eloquentjavascript.net/16_canvas.html#p_sX9ENmCEO9)Because shapes on a canvas are just pixels, after we draw them, there is no way to move them (or remove them). The only way to update the canvas display is to clear it and redraw the scene. [](http://eloquentjavascript.net/16_canvas.html#p_Kn4mJDmduk)The?`updateViewport`?method is similar to?`DOMDisplay`'s`scrollPlayerIntoView`?method. It checks whether the player is too close to the edge of the screen and moves the viewport when this is the case. ~~~ CanvasDisplay.prototype.updateViewport = function() { var view = this.viewport, margin = view.width / 3; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)); if (center.x < view.left + margin) view.left = Math.max(center.x - margin, 0); else if (center.x > view.left + view.width - margin) view.left = Math.min(center.x + margin - view.width, this.level.width - view.width); if (center.y < view.top + margin) view.top = Math.max(center.y - margin, 0); else if (center.y > view.top + view.height - margin) view.top = Math.min(center.y + margin - view.height, this.level.height - view.height); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_qSUMwlAFeW)The calls to?`Math.max`?and?`Math.min`?ensure that the viewport does not end up showing space outside of the level.?`Math.max(x, 0)`?ensures that the resulting number is not less than zero.?`Math.min`, similarly, ensures a value stays below a given bound. [](http://eloquentjavascript.net/16_canvas.html#p_uxQ4EAtiQh)When clearing the display, we’ll use a slightly different color depending on whether the game is won (brighter) or lost (darker). ~~~ CanvasDisplay.prototype.clearDisplay = function() { if (this.level.status == "won") this.cx.fillStyle = "rgb(68, 191, 255)"; else if (this.level.status == "lost") this.cx.fillStyle = "rgb(44, 136, 214)"; else this.cx.fillStyle = "rgb(52, 166, 251)"; this.cx.fillRect(0, 0, this.canvas.width, this.canvas.height); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_TjOnlogmqf)To draw the background, we run through the tiles that are visible in the current viewport, using the same trick used in?`obstacleAt`?in the?[previous chapter](http://eloquentjavascript.net/15_game.html#viewport). ~~~ var otherSprites = document.createElement("img"); otherSprites.src = "img/sprites.png"; CanvasDisplay.prototype.drawBackground = function() { var view = this.viewport; var xStart = Math.floor(view.left); var xEnd = Math.ceil(view.left + view.width); var yStart = Math.floor(view.top); var yEnd = Math.ceil(view.top + view.height); for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var tile = this.level.grid[y][x]; if (tile == null) continue; var screenX = (x - view.left) * scale; var screenY = (y - view.top) * scale; var tileX = tile == "lava" ? scale : 0; this.cx.drawImage(otherSprites, tileX, 0, scale, scale, screenX, screenY, scale, scale); } } }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_exCLyNQQL6)Tiles that are not empty (null) are drawn with?`drawImage`. The?`otherSprites`image contains the pictures used for elements other than the player. It contains, from left to right, the wall tile, the lava tile, and the sprite for a coin. ![](https://box.kancloud.cn/2015-10-31_563439aa3e087.png) [](http://eloquentjavascript.net/16_canvas.html#p_1R4HdSFR8E)Background tiles are 20 by 20 pixels, since we will use the same scale that we used in?`DOMDisplay`. Thus, the offset for lava tiles is 20 (the value of the?`scale`variable), and the offset for walls is 0. [](http://eloquentjavascript.net/16_canvas.html#p_KIwkr/Frrz)We don’t bother waiting for the sprite image to load. Calling?`drawImage`?with an image that hasn’t been loaded yet will simply do nothing. Thus, we might fail to draw the game properly for the first few frames, while the image is still loading, but that is not a serious problem. Since we keep updating the screen, the correct scene will appear as soon as the loading finishes. [](http://eloquentjavascript.net/16_canvas.html#p_/1DXjZpAsq)The walking character shown earlier will be used to represent the player. The code that draws it needs to pick the right sprite and direction based on the player’s current motion. The first eight sprites contain a walking animation. When the player is moving along a floor, we cycle through them based on the display’s?`animationTime`?property. This is measured in seconds, and we want to switch frames 12 times per second, so the time is multiplied by 12 first. When the player is standing still, we draw the ninth sprite. During jumps, which are recognized by the fact that the vertical speed is not zero, we use the tenth, rightmost sprite. [](http://eloquentjavascript.net/16_canvas.html#p_wuDMxn8vbx)Because the sprites are slightly wider than the player object—24 instead of 16 pixels, to allow some space for feet and arms—the method has to adjust the x-coordinate and width by a given amount (`playerXOverlap`). ~~~ var playerSprites = document.createElement("img"); playerSprites.src = "img/player.png"; var playerXOverlap = 4; CanvasDisplay.prototype.drawPlayer = function(x, y, width, height) { var sprite = 8, player = this.level.player; width += playerXOverlap * 2; x -= playerXOverlap; if (player.speed.x != 0) this.flipPlayer = player.speed.x < 0; if (player.speed.y != 0) sprite = 9; else if (player.speed.x != 0) sprite = Math.floor(this.animationTime * 12) % 8; this.cx.save(); if (this.flipPlayer) flipHorizontally(this.cx, x + width / 2); this.cx.drawImage(playerSprites, sprite * width, 0, width, height, x, y, width, height); this.cx.restore(); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_gswQ2kj9pD)The?`drawPlayer`?method is called by?`drawActors`, which is responsible for drawing all the actors in the game. ~~~ CanvasDisplay.prototype.drawActors = function() { this.level.actors.forEach(function(actor) { var width = actor.size.x * scale; var height = actor.size.y * scale; var x = (actor.pos.x - this.viewport.left) * scale; var y = (actor.pos.y - this.viewport.top) * scale; if (actor.type == "player") { this.drawPlayer(x, y, width, height); } else { var tileX = (actor.type == "coin" ? 2 : 1) * scale; this.cx.drawImage(otherSprites, tileX, 0, width, height, x, y, width, height); } }, this); }; ~~~ [](http://eloquentjavascript.net/16_canvas.html#p_PD+LPXwCgk)When drawing something that is not the player, we look at its type to find the offset of the correct sprite. The lava tile is found at offset 20, and the coin sprite is found at 40 (two times?`scale`). [](http://eloquentjavascript.net/16_canvas.html#p_e6Z9O4bib+)We have to subtract the viewport’s position when computing the actor’s position since (0,0) on our canvas corresponds to the top left of the viewport, not the top left of the level. We could also have used?`translate`?for this. Either way works. [](http://eloquentjavascript.net/16_canvas.html#p_4VReSkKraV)The tiny document shown next plugs the new display into?`runGame`: ~~~ <body> <script> runGame(GAME_LEVELS, CanvasDisplay); </script> </body> ~~~ ## [](http://eloquentjavascript.net/16_canvas.html#h_T6CfghQSfx)Choosing a graphics interface [](http://eloquentjavascript.net/16_canvas.html#p_hiYOCc2tar)Whenever you need to generate graphics in the browser, you can choose between plain HTML, SVG, and canvas. There is no single?*best*?approach that works in all situations. Each option has strengths and weaknesses. [](http://eloquentjavascript.net/16_canvas.html#p_O7BQaYbnwn)Plain HTML has the advantage of being simple. It also integrates well with text. Both SVG and canvas allow you to draw text, but they won’t help you position that text or wrap it when it takes up more than one line. In an HTML-based picture, it is easy to include blocks of text. [](http://eloquentjavascript.net/16_canvas.html#p_6uH6l730WJ)SVG can be used to produce crisp graphics that look good at any zoom level. It is more difficult to use than plain HTML but also much more powerful. [](http://eloquentjavascript.net/16_canvas.html#p_J5B7lFXtzE)Both SVG and HTML build up a data structure (the DOM) that represents the picture. This makes it possible to modify elements after they are drawn. If you need to repeatedly change a small part of a big picture in response to what the user is doing or as part of an animation, doing it in a canvas can be needlessly expensive. The DOM also allows us to register mouse event handlers on every element in the picture (even on shapes drawn with SVG). You can’t do that with canvas. [](http://eloquentjavascript.net/16_canvas.html#p_7N4OSlG5eS)But canvas’s pixel-oriented approach can be an advantage when drawing a huge amount of tiny elements. The fact that it does not build up a data structure but only repeatedly draws onto the same pixel surface gives canvas a lower cost per shape. [](http://eloquentjavascript.net/16_canvas.html#p_6P4A05RVAk)There are also effects, such as rendering a scene one pixel at a time (for example, using a ray tracer) or postprocessing an image with JavaScript (blurring or distorting it), that can only be realistically handled by a pixel-based technique. [](http://eloquentjavascript.net/16_canvas.html#p_lecWO5A8ae)In some cases, you may want to combine several of these techniques. For example, you might draw a graph with SVG or canvas but show textual information by positioning an HTML element on top of the picture. [](http://eloquentjavascript.net/16_canvas.html#p_nXWEbGkTBm)For nondemanding applications, it really doesn’t matter much which interface you choose. The?[second display](http://eloquentjavascript.net/16_canvas.html#canvasdisplay)?we built for our game in this chapter could have been implemented using any of these three graphics technologies since it does not need to draw text, handle mouse interaction, or work with an extraordinarily large amount of elements. ## [](http://eloquentjavascript.net/16_canvas.html#h_ErccPg/l98)Summary [](http://eloquentjavascript.net/16_canvas.html#p_tJH36yWkVt)In this chapter, we discussed techniques for drawing graphics in the browser, focusing on the?`<canvas>`?element. [](http://eloquentjavascript.net/16_canvas.html#p_zTR1kU1R2F)A canvas node represents an area in a document that our program may draw on. This drawing is done through a drawing context object, created with the`getContext`?method. [](http://eloquentjavascript.net/16_canvas.html#p_77n8vdtsoS)The 2D drawing interface allows us to fill and stroke various shapes. The context’s?`fillStyle`?property determines how shapes are filled. The`strokeStyle`?and?`lineWidth`?properties control the way lines are drawn. [](http://eloquentjavascript.net/16_canvas.html#p_p9lwBrZeDB)Rectangles and pieces of text can be drawn with a single method call. The`fillRect`?and?`strokeRect`?methods draw rectangles, and the?`fillText`?and`strokeText`?methods draw text. To create custom shapes, we must first build up a path. [](http://eloquentjavascript.net/16_canvas.html#p_+WESqzV0sH)Calling?`beginPath`?starts a new path. A number of other methods add lines and curves to the current path. For example,?`lineTo`?can add a straight line. When a path is finished, it can be filled with the?`fill`?method or stroked with the?`stroke`?method. [](http://eloquentjavascript.net/16_canvas.html#p_DjIq4BWBzl)Moving pixels from an image or another canvas onto our canvas is done with the?`drawImage`?method. By default, this method draws the whole source image, but by giving it more parameters, you can copy a specific area of the image. We used this for our game by copying individual poses of the game character out of an image that contained many such poses. [](http://eloquentjavascript.net/16_canvas.html#p_EWHXtIfgMN)Transformations allow you to draw a shape in multiple orientations. A 2D drawing context has a current transformation that can be changed with the`translate`,?`scale`, and?`rotate`?methods. These will affect all subsequent drawing operations. A transformation state can be saved with the?`save`?method and restored with the?`restore`?method. [](http://eloquentjavascript.net/16_canvas.html#p_CiZHyGysLU)When drawing an animation on a canvas, the?`clearRect`?method can be used to clear part of the canvas before redrawing it. ## [](http://eloquentjavascript.net/16_canvas.html#h_TcUD2vzyMe)Exercises ### [](http://eloquentjavascript.net/16_canvas.html#h_sZheOHQF5N)Shapes [](http://eloquentjavascript.net/16_canvas.html#p_ML2Sk/PrfT)Write a program that draws the following shapes on a canvas: 1. [](http://eloquentjavascript.net/16_canvas.html#p_jg9BlkxnI6)A trapezoid (a rectangle that is wider on one side) 2. [](http://eloquentjavascript.net/16_canvas.html#p_TsmIPWkWaZ)A red diamond (a rectangle rotated 45 degrees or ?π radians) 3. [](http://eloquentjavascript.net/16_canvas.html#p_rB8lM2f3nJ)A zigzagging line 4. [](http://eloquentjavascript.net/16_canvas.html#p_giU72/tC1m)A spiral made up of 100 straight line segments 5. [](http://eloquentjavascript.net/16_canvas.html#p_BaFPNufkG9)A yellow star ![](https://box.kancloud.cn/2015-10-31_563439aa495e7.png) [](http://eloquentjavascript.net/16_canvas.html#p_rGwDFrV/8d)When drawing the last two, you may want to refer to the explanation of`Math.cos`?and?`Math.sin`?in?[Chapter 13](http://eloquentjavascript.net/13_dom.html#sin_cos), which describes how to get coordinates on a circle using these functions. [](http://eloquentjavascript.net/16_canvas.html#p_8n4Hu9tdGs)I recommend creating a function for each shape. Pass the position, and optionally other properties, such as the size or the number of points, as parameters. The alternative, which is to hard-code numbers all over your code, tends to make the code needlessly hard to read and modify. ~~~ <canvas width="600" height="200"></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); // Your code here. </script> ~~~ ### [](http://eloquentjavascript.net/16_canvas.html#h_bJrtZj5liF)The pie chart [](http://eloquentjavascript.net/16_canvas.html#p_5/ovyOQpmP)[Earlier](http://eloquentjavascript.net/16_canvas.html#pie_chart)?in the chapter, we saw an example program that drew a pie chart. Modify this program so that the name of each category is shown next to the slice that represents it. Try to find a pleasing-looking way to automatically position this text, which would work for other data sets as well. You may assume that categories are no smaller than 5 percent (that is, there won’t be a bunch of tiny ones next to each other). [](http://eloquentjavascript.net/16_canvas.html#p_nTwqKbJWIc)You might again need?`Math.sin`?and?`Math.cos`, as described in the previous exercise. ~~~ <canvas width="600" height="300"></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var total = results.reduce(function(sum, choice) { return sum + choice.count; }, 0); var currentAngle = -0.5 * Math.PI; var centerX = 300, centerY = 150; // Add code to draw the slice labels in this loop. results.forEach(function(result) { var sliceAngle = (result.count / total) * 2 * Math.PI; cx.beginPath(); cx.arc(centerX, centerY, 100, currentAngle, currentAngle + sliceAngle); currentAngle += sliceAngle; cx.lineTo(centerX, centerY); cx.fillStyle = result.color; cx.fill(); }); </script> ~~~ ### [](http://eloquentjavascript.net/16_canvas.html#h_IoBBN8CiQ5)A bouncing ball [](http://eloquentjavascript.net/16_canvas.html#p_QtPClJ4kuI)Use the?`requestAnimationFrame`?technique that we saw in?[Chapter 13](http://eloquentjavascript.net/13_dom.html#animationFrame)?and[Chapter 15](http://eloquentjavascript.net/15_game.html#runAnimation)?to draw a box with a bouncing ball in it. The ball moves at a constant speed and bounces off the box’s sides when it hits them. ~~~ <canvas width="400" height="400"></canvas> <script> var cx = document.querySelector("canvas").getContext("2d"); var lastTime = null; function frame(time) { if (lastTime != null) updateAnimation(Math.min(100, time - lastTime) / 1000); lastTime = time; requestAnimationFrame(frame); } requestAnimationFrame(frame); function updateAnimation(step) { // Your code here. } </script> ~~~ ### [](http://eloquentjavascript.net/16_canvas.html#h_3ePcd0S4v0)Precomputed mirroring [](http://eloquentjavascript.net/16_canvas.html#p_onlMAFaFji)One unfortunate thing about transformations is that they slow down drawing of bitmaps. For vector graphics, the effect is less serious since only a few points (for example, the center of a circle) need to be transformed, after which drawing can happen as normal. For a bitmap image, the position of each pixel has to be transformed, and though it is possible that browsers will get more clever about this in the future, this currently causes a measurable increase in the time it takes to draw a bitmap. [](http://eloquentjavascript.net/16_canvas.html#p_b50ouk4znE)In a game like ours, where we are drawing only a single transformed sprite, this is a nonissue. But imagine that we need to draw hundreds of characters or thousands of rotating particles from an explosion. [](http://eloquentjavascript.net/16_canvas.html#p_WgNsgEoaNe)Think of a way to allow us to draw an inverted character without loading additional image files and without having to make transformed?`drawImage`calls every frame. The key to the solution is the fact that we can use a canvas element as a source image when using?`drawImage`. It is possible to create an extra?`<canvas>`element, without adding it to the document, and draw our inverted sprites to it, once. When drawing an actual frame, we just copy the already inverted sprites to the main canvas. [](http://eloquentjavascript.net/16_canvas.html#p_MbtRzmupcp)Some care would be required because images do not load instantly. We do the inverted drawing only once, and if we do it before the image loads, it won’t draw anything. A?`"load"`?handler on the image can be used to draw the inverted images to the extra canvas. This canvas can be used as a drawing source immediately (it’ll simply be blank until we draw the character onto it).
人妻中出视频一区二区| 99.com精品视频| 熟女少妇视频一区二区在线| 国产精品久久久久国产| 日韩av电影在线观看的| 蜜臀av 麻豆av| 亚洲aⅴ欧美综合一区二区三区| 日本午夜在线免费观看| 欧美一区二区三区视频午夜| 日本熟妇乱子伦a片在线观看 | 色丁香婷婷综合久久| 99精品视频69v精品视频| 玖玖资源站中文字幕一区二区| 日韩欧美综合一区二区在线| 七十路熟女俱乐部| 成人精品1024欧美日韩| av青青草原在线观看| 久久99精品国产91| 亚洲熟妇av日韩熟妇在线| 色综合精品一区二区三区| 3d动漫一区二区在线观看| 亚洲视频欧美视频另类| 国产精品99久久电影| 亚洲日本韩国欧美一起| 亚洲一区视频大全| 国产又大又猛又黄的视频| 国产 另类 在线 欧美日韩| 亚洲不卡一区av| 欧美自拍 亚洲偷拍| 日韩电影黄色免费| 五月天色图婷婷亚洲| 久久综合久久综合大香蕉| 国产99不卡免费在线观看| 亚洲一线产区二线产区区| 一本色道久久亚洲综合精品蜜桃| 人妻av不卡一区二区三区| 国产 欧美 日韩 视频| 777亚洲精品乱码久久| 中文字幕精品久久久人妻| 欧美三级中文字幕久久精品| 中文字幕av最新资源| 亚洲精品蜜桃久久久久| 色丁香婷婷综合久久| 亚洲最黄色的视频| 久久综合中文字幕一区二区三区| 亚洲女人中文字幕在线| 日韩不卡高清视频| 亚洲五月婷婷视频| 精品一区二区三区四区人妻69 | 我要看一级国产黄色绿像| 免费中文字幕在线播放| 午夜寻花美女在线| 婷婷激情五月天图片| 亚洲欧洲黄色图区| 日韩欧美在线视频第一页| 国产成人无码AA精品一区 | 久久视频黄色观看网站| 午夜精品久久久久久久久二区三区| 3d动漫一区二区在线观看| 精品一区二区三区蜜桃臀的优势| 亚洲av不卡码在线看| 少妇人妻视频在线观看| 亚洲阿v天堂久久| 国产一区二区三区黄色| 欧美一区二区理论片在线观看| 午夜日韩麻豆福利| 精品一区二区三区四区人妻69| 牛牛成人手机视频在线| 2019年中文字幕在线看| 制服巨乳人妻在线| 亚洲欧美在线视频播放| 欧美人妻激情中文视频| 草裙成人精品一区二区三区| av一级二级三级久久久| 亚洲男人五月天堂| 亚洲国模私拍视频| 91综合精品国产丝袜长腿| 中文字幕一区二区三区六区9区| 麻豆免费国产福利免费国产福利| 久久亚洲精品日韩高清| 在线观看色有小视频| 久久久久久久久久久最新| 五月综合缴情婷婷六月| 精品国产麻豆免费成人网| 成年女人晚上碰免费视频| wwxxx中文字幕| 欧美日韩在线卡一卡二卡三| 国产精品成人av麻豆| 亚洲少妇黄色一级片| 欧美日韩国产色图视频| 91久久亚洲成人精品| 99精品高清视频一区二区| 精品人妻二区中文字幕| 国产精品一二三四在线| 欧美日韩精品综合在线一区二区| 天天干天天插天天操天天日 | 久久久免费精品少妇| 色噜噜狠狠躁夜夜躁| 日韩国产91综合精品| 久久 99 精品视频| 91大神精品大长腿在线观看| 久久久久在线免费看| 99re在线精品国产欧美久久| 国产又粗又猛又爽又黄的刘涛视| 最新精品成人在线| 18禁成年av网站免费看| huangse网站在线观看| 日韩成人av在线电影观看| 麻豆丝袜美女人妻中文| 亚洲最大成人网一区二区三区| 亚洲精品乱码97久久久久久| 精品久久一区电影亚洲| 色图av亚洲综合| 视频免费在线观看91| 日韩av电影com| 青青91免费视频| 人妻日韩精品在线观看视频| 国内精品久久久久久久久久清纯| 成人av电影免费版| 日本精品久久久久中人妻| 精品国产乱码久久久人妻| 日韩av 自拍偷拍| 另类h小视频在线观看| 人妻熟女视频免费观看| 99re6热在线视频免费观看| 人妻中出视频一区二区| 中文字幕av最新在线| 99久久久免费毛片基地| 青青青高清国产视频| 日本高清不卡视频在线播放| 人妻一区二区三区巨免费| 玖玖资源站中文字幕一区二区| 欧美一区二区理论片在线观看| 人人妻人人澡人人爽dv| 亚洲欧美av在线观看| 精品人妻二区中文字幕| 篠田优中文字幕在线播| 91人妻人人妻人人爽| 91超碰免费在线播放| 又爽又黄的免费视频91| 欧美三级中文字幕久久精品| 67914熟女在线观看| 欧美熟女vides| 久久午夜福利电影免费试看| 亚洲av高清不卡| 亚洲成a人片,77777| 香蕉久久a v一区二区三区| 欧美自拍 亚洲偷拍| 天天色综合天天射综合| 新版天堂av资源在线| 99免费观看视频在线| 五月激情婷婷网络| 中文字幕婷婷网站| 午夜三级中文字幕视频网址大全| av在线免费播放成人| 日本老女人视频在线观看| 日韩大全毛片免费观看视频| 亚洲视频欧美视频另类| 亚洲另类色区欧美日韩| 精品乱码久久久久久蜜臀| 人妻啪啪视频免费看| 亚洲欧美日韩一区17c| 国产乱子伦视频免费| 色综合久久久久久久久| 日韩美女在线视频一区| 91国偷自产一区二区三区老熟女| 亚洲国产精品久久久久婷婷av| 久久99国产综合精品无码免费| 精品成人1区2区3区在线看片| 99日本亚洲精品视频| 99久久极品蜜桃臀精品久久| 久久综合中文字幕一区二区三区| 免费看久久久久久久性大片| 美国av 在线播放| 日韩av在线精品观看| 欧美日本一道本一区二区| 亚洲av狠狠地操你| 欧美视频 亚洲视频| 午夜影院av在线| 玖玖资源站中文字幕一区二区| 国产日韩欧美人妻在线观看| 99热这里只有精品网| 中文字幕日韩天堂| 美女差点操死在线观看| 人人妻人人澡人人爽国产一区| 97久久视频免费在线播放| 麻豆丝袜美女人妻中文| 91偷伦一区二区三区蜜臀| 久久久精品欧美一区二区国产| 中文字幕av最新资源| 久久久久999蜜桃视频| 国产精品午夜蜜av| 大香蕉大香蕉大香蕉大香蕉大| 丰满人妻一区二区三区视频看看 | 国产精品色呦呦视频免费看| 国产日韩欧美成人一二三区| 精品久久久久久18免费网站| 久久视频在线免费播放| 日韩av电影com| 欧美爱情动作片在线一区| 美女差点操死在线观看| 新版天堂av资源在线| 亚洲日韩视频高清在线观看| 亚洲一区视频大全| 亚洲欧美国产另类91综合| 瑟瑟网站免费观看| 欧美中文字幕视频网| 国产aⅴ爽av久久久久成| 久久久亚洲在线视频| 99免费观看视频在线| 亚洲伊人网在线播放| 久久av资源男人站| 日韩成人av影视在线观看| 大香蕉久草网一区二区三区| 日韩精品综合视频在线 | 午夜三级中文字幕视频网址大全| 亚洲一区视频大全| 精品人妻一区二区三区√| 久久热最新视频在线观看| 麻豆精品123在线观看| 天天插天天日天天舔| 91麻豆免费国产在线| 久久久久久久久人妻精品| 久久九特黄的免费大片| 之久精品一区二区| 亚洲天堂网av中文字幕| 国产99成人自拍视频| 大香蕉久草网一区二区三区| 日本黄页在线播放日本网站| 日本av都有哪些系列| 人妻一区二区中文字幕在线| 少妇人妻成人在线| 日韩手机在线一区二区三区| w疯狂的少妇2做爰韩国| 综合激情伊人久久| 亚洲一区二区三区久久aa| 日本女优和黑人的| av一级二级三级久久久| 欧美老熟妇重口另类xxx| 久久亚洲熟妇中文字幕| 97精品国产自在在线观看蜜臀| 少妇人妻一区2区中文字幕| 亚洲香蕉av电影| 香蕉在线蕉久在线| 亚洲少妇黄色一级片| 久久精品熟女人妻一区二区三区| 久久久96精品久久久| 中文字幕av最新资源| 天天摸天天舔天天爱| 亚洲五月婷婷视频| 热久久免费频精品18| 亚洲国产一成人久久精品| 国产91久久久久久久| 亚洲av高清不卡| 国产亚洲av免费一区二区| 精品一区二区国产在线观看| 中文字幕一区二区不卡顿| 美国av 在线播放| 91超碰熟女在线97| 日韩大全毛片免费观看视频| 久久久久久最新精品| 伊人久久中文字幕综合观看| 五月黄色激情视频| 男人的天堂久久精平| 亚洲女人老师毛茸茸| 亚洲国产欧美日本视频| 亚洲天堂黄色在线| 久久久久久久久久久最新| 色综合久久久久久久久| 青青草成人影院在线观看| 久久久精品99国产国产精| 十八禁久久久久久久久久久久久久| 国产精品久久精品一区二区三区| 97精品久久人人爽人人爽| 欧美精品一区二区在线视频| 日本亚洲天堂久久| 亚洲欧洲偷拍自拍| 精品久久久久久999| 97国产人妻换人妻| 久久精品中文闷骚内射| 亚洲一区二区三区久久aa| 四十路av熟女俱乐部| 精品国产乱码久久久久久蜜坠欲下| 亚洲视频另类专区| 天天色天天干天天舔| 美女在线观看亚洲一区| 欧美日韩一区二区综合视频| 五月天丁香婷婷国产| 日韩视频在线播放一区二区| 91麻豆精品91久久久久同性| 午夜寻花美女在线| 久久精品久久一区二区三区| 久久久久久精品国产成人| 日本中文国产字幕| 91人妻精品久久久久久久久| 亚洲av不卡码在线看| 麻豆网站在线免费看| 天堂av最新资源在线| 国产欧美日韩亚洲另类第一第二页| 久久久久久久久久久久久熟女a∨ 精品99国内中文字幕 | 福利精品视频免费观看| 久久av资源男人站| 欧美日韩少妇一二三| 色哟哟免费在线播放| 日韩成人av在线电影观看| 色丁香婷婷综合久久| 中文字幕av最新在线| 日韩精品少妇人妻熟女| 日韩毛片亚洲av| 久久99国产综合精品无码免费| 1024 国产高清の最新合集| 91老司机免费福利| 亚洲国产精品国自产拍av麻豆| 久久这里只有精品久| 18禁成年av网站免费看| 色噜噜狠狠躁夜夜躁| 欧美日韩v中文在线| 欧美3d成人动漫在线| 亚洲成年人黄色片| 亚洲 欧美 制服 人妻| 久久精品 中文字幕| 中文字幕在线有码二区| 青青草国产福利视频| 日韩精品综合免费视频| 国产有码av一区二区| 91中日在线中文字幕| 亚洲av网站女性向在线观看| 久久亚洲熟妇中文字幕| 国产精品男人的天堂999| 人妻日韩精品在线观看视频| 欧美日韩国产色图视频| 人妻精品少妇嫩草麻豆| 国语黄色淫秽录像带| 精品国产精品视频免费在线观看| 亚洲欧美日产人妻| 亚洲激情视频在线视频| 精品人妻一区二区三区线国色天香| 国产又大又长又粗又硬免费视频| 91沈先生探花极品在线| 日韩欧美在线视频第一页| 日韩欧美亚州综合久久| 日韩黄色一级大片| 亚洲av电影在线一区二区| 日韩亚洲图色在线| 久久久久久少妇被弄高潮| 热久久免费频精品18| 久久一本麻豆天美欧美日韩| 另类欧美亚洲中文综合| 国产成人精品久久综合| 国产清纯白嫩美女无套| 欧美激情电影在线观看不卡| 亚洲欧美日韩一区二区三区不卡| 日韩av在线观看卡一卡| 日韩美女主播人体视频自拍首页| 日韩乱妇乱女熟妇熟女网红| 亚洲自拍偷拍色图区| 国产人妻另类综合专区| 最新美女激情av| 福利一福利二福利三| 欧美熟女人妻一区二区三区| 青青草成人免费电影| 中文字幕一线一区和二区| 日韩av在线不卡网站| 国产99不卡免费在线观看| 亚洲精品乱码97久久久久久| av激情网站在线观看| 亚洲制服高清中文字幕| 麻豆丝袜美女人妻中文| 99精品视频69v精品视频| 91成人国产精品视频| 久久精品中文闷骚内射| 天天射天天舔天天射| 亚洲欧美自拍偷拍在线观看| 精品欧美乱子伦一区二区三区 | 一区二区三区内射美女毛片| 婷婷中文字幕色婷婷| 天天做天天舔天天射| 国产精品久久精品一区二区三区| 高清一区二区三区日本4| 久久 99 精品视频| 99re6热在线视频免费观看| 久久国产精品——国产精品 | 一级片一级片久久精品电影网电影 | 一区二区三区四区中文字幕| 日韩欧美亚州综合久久| 日韩成人免费电影三区| 中文字幕精品久久久人妻| av在线免费播放成人| 成人免费av在线网站| 一本一道久久a久久久| 久久久久女优免费视频| 久久久久久久久久久最新| 国产成人无码AA精品一区| 99re6热在线视频免费观看| 亚洲精选黄色在线观看| 国产成人av吴梦梦视频| 高清视频在线观看一区二区三区 | 日本最新中文字幕| 日本女同性恋视频| 精品欧美乱子伦一区二区三区| 厕所偷拍视频一区二区三区| 亚洲aⅴ欧美综合一区二区三区| 婷婷丁香花五月天| 亚洲激情有码一区二区| 日本vs欧美一区二区三区| 欧美熟妇人妻视频在线| 99re6热在线视频免费观看| 一区二区三区乱码视频| 亚洲五月婷婷视频| 五月婷久久综合狠狠爱97| 久久精品久久一区二区三区| 日韩av午夜免费| 五月天丁香婷婷国产| 日韩欧美颜色渔网| 欧美肥臀精品一区二区三区| 日韩欧美xxxx大片| 少妇高潮一区二区三区99欧美| 91偷伦一区二区三区蜜臀| 精品一区二区三区四区人妻69| 色哟哟免费在线播放| 日韩精品福利性无码专区| 99精品高清视频一区二区| 高清一区二区日韩视频精品| 麻豆精品一区在线免费观看| 中文字幕一线一区和二区| 久久久久999久久日| 久久久国产一区二区视| 久久99精品久久久久久三级| 伊人久久婷婷av| 中文字幕成人资源网站| 99久久窝窝午夜影视| 国产美女深喉口爆吞精免费| 91九色porny国产视频| jizzhd国产剧情| 中文字幕日韩天堂| 亚洲国模私拍视频| 91久久亚洲成人精品| 欧美熟妇精品在线观看| 偷偷夜夜精品一区二区三区蜜桃| 中文字幕精品久久伊人| 精品欧美乱子伦一区二区三区| 蜜桃久久久一区二区三区| 3d动漫一区二区在线观看| 亚洲激情视频在线视频| 91久久国产综合久久91在线| 久久午夜福利电影免费试看| 亚洲欧美在线视频播放| 精品久久久久久亚洲国产999| 日韩午夜激情福利免费| 男人的天堂久久久亚洲| 国产a级片免费在线观看| 88在线观看91蜜桃国自产 | 精品日韩色国产在线观看| 99re6热在线视频免费观看| 亚洲熟妇av日韩熟妇在线| 日韩av在线不卡网站| 国产一区二区视频大全床| wwxxx中文字幕| 92视频影院视频影院麻豆| 另类h小视频在线观看| 成年女人晚上碰免费视频| 欧美中文字幕视频网| 嫩草一区二区三区四区中文| 久久伊人亚洲综合网| 欧美一二三区在线观看| 福利一福利二福利三| 国产91久久精品一区二区老| 国产又大又长又粗又硬免费视频 | 999精品插丰满少妇人妻| 蜜桃av 1区二区| 国产 另类 在线 欧美日韩| 综合 另类 自拍| 久久久精品99国产国产精| 精品美女1区2区3区| 亚洲女人中文字幕在线| 国产欧美精品久久无广告| 国产精品色呦呦视频免费看| 中文字幕一线一区和二区| 日韩国产精品电影网| 少妇惨叫久久久久久久| 男人亚洲天堂2018| 亚洲欧美国产另类91综合| 精品乱码一区二区三区四区| 亚洲制服高清中文字幕| 久久久久久一欧美国产| av日韩在线免费观看网站| 亚洲精品图片第十八页| 婷婷亚洲天堂中文字幕| 日本18禁片免费久久| 欧美不卡高清一区二区三区| 另类欧美亚洲中文综合| 日韩欧美亚州综合久久| 国产成人精品久久久久久蜜臀| 人人妻人人澡人人看| 热久久免费频精品18| 一本一道久久a久久久| 日韩av在线观看免费看看| 蜜臀av一区二区三区人妻少妇| 成人黄色午夜污网站在线观看| 91九色蝌蚪熟妇出轨| 蜜桃精品一区二区三区在| 青青91免费视频| 大尺度av一区二区三区| 久久久久久久久久久最新| 日韩欧美在线视频第一页| 中文字幕 日本伊人| 日本高清不卡视频在线播放| 亚洲不卡一区av| 日韩久久久三级电影| 国产欧美日韩精品专区黑人| 久久婷婷亚洲中文一区二区 | 四虎网站免费av| 久久99国产综合精品无码免费| 91免费版下载成人| 人妻中出视频一区二区| 亚洲激情网在线播放| 日本18禁片免费久久| 青青草国产福利视频| 国产91在线播放精品| 热久久免费频精品18| 日本少妇人妻xxxx| 92视频影院视频影院麻豆| 午夜在线看1000集| 韩国女团午夜大尺度福利| 久久精品色婷婷国产网站| 国产成人精品久久久女| 精品国产丝袜久久久一区二区 | 久久久久久久免费女人体| 亚洲aⅴ欧美综合一区二区三区| 欧美中文字幕久久久| 精品欧美日韩国产在线| 日韩美女影院免费在线观看| 青青草成人免费电影| 麻豆成人在线免费观看视频 | 日韩亚洲丝袜系列| 国产精品男人的天堂999| 欧美激情欧美情色成人在线| 日本二次元少女裸| 少妇人妻一区2区中文字幕| 99.com精品视频| 亚洲日本精彩视频在线观看| 亚洲乱精品中文字字幕| 久久热最新视频在线观看| 99爱99久久久久久久久久| 日本av都有哪些系列| 亚洲 自拍偷拍 欧美| 欧美三级中文字幕久久精品| 亚洲欧美一二三视频| 国产又粗又黄又大又长视频| 青草青草视频免费2在线观看| 国产一区二区三区女同| 亚洲成人影院中文字幕| 中文字幕在线有码二区| 日韩a毛片视频免费看| 中文字幕+人妻熟女| 老熟女17页一91| 精品蜜臀久久久久抄底| 国产清纯白嫩美女无套| 2001年亚洲区十强赛| 色婷婷综合免费在线视频| 久久国产精品久久伊人麻豆| 色男人天堂东京热| 亚洲人妻中文字幕av首页| 免费av网站在线浏览| 久久草免费在线视频| 99久久999久久久精品综合| 亚洲av色图com| 麻豆免费国产福利免费国产福利| 欧美不卡高清一区二区三区| 日韩av在线观看卡一卡| av岛国不卡在线观看| 国产a级片免费在线观看| 日本黄色成年视频| 久久久久国产亚洲av麻豆| 丝袜日韩中文字幕| 亚洲香蕉av电影| 在线观看色有小视频| 中文字幕亚洲资源天堂| 欧美一区二区理论片在线观看| 久久精品 中文字幕| 蜜桃视频在线观看一区精品| 亚洲男人五月天堂| 国产精品久久久久久久久久网站| 999热精品在线观看| 国产精品久久久久一区二区| 国产69tv精品久久| 久久久精品欧美一区二区国产| 综合激情五月天久久| 国产成人无码AA精品一区| av日韩中文字幕人妻| 国产精品欧美日韩五月香蕉| 黑人粗大精品一区二区| 久久久97精品国产| 久久精品人妻中文字幕一区| 久久久亚洲熟妇熟女ⅹx| 久久亚麻亚洲蜜桃臀| 日韩av在线点播| 91超碰熟女在线97| 成年人在线免费观看黄色片| 国产丝袜香蕉在线观看| 人妻日韩精品在线观看视频| 激情一区日韩一区欧美一区| 欧美日本一道本一区二区| 久久久久久久久人妻精品| 麻豆网站在线免费看| 亚洲aⅴ欧美综合一区二区三区| 日韩 美女 在线观看| 亚洲激情 欧美激情| 人妻一区二区中文字幕在线| 激情四射五月开心六月婷婷| 91久久婷婷国产麻豆精品| 日韩av福利大片在线观看| 日本熟妇乱子伦a片在线观看| 亚洲一区二区三区久久aa| 国产又粗又黄又大又长视频| 99久久999久久久精品综合| 精品97人妻无码中文永久| 国产成人精品久久久久久蜜臀| 久久久成人在线免费视频 | 俺去鲁婷婷六月色综合| 亚洲欧美在线制服丝袜| 亚洲成人久久久久| 99久久极品蜜桃臀精品久久| 亚洲国产aⅴ精品一区二区欧美| 亚洲欧洲国产一区二区| 亚洲国产一成人久久精品| 少妇惨叫久久久久久久| 丰满人妻av白石茉莉奈电影| 亚洲国产精品午夜av| 日韩亚洲图色在线| 亚洲 欧美 制服 人妻| 久久综合久久综合大香蕉| sepapa自拍偷拍| 国产成人无码AA精品一区 | 91成人国产精品视频| 亚洲一区二区三二区厕所偷拍| 日韩成视频在线播放| 久久五月婷婷综合视频| 97人人人欧美人人妻人人| 91在线精品国自产拍| 宅女午夜av福利影视 | 国产黄色主播网址大全在线播放| 国产99成人自拍视频| 午夜精品福利小视频| 久久国产精品色av免费看| 大香蕉大香蕉大香蕉大香蕉大| 国语黄色淫秽录像带| 少妇人妻视频在线观看| 日韩av 自拍偷拍| 亚洲:西西一区二区三区四区五区| 国产精品色呦呦视频免费看| 国产一区二区三区黄色| 欧美高清在线视频99| 欧美三级中文字幕久久精品| 午夜三级中文字幕视频网址大全| 亚洲人的午夜影院| 安斋拉拉中文字幕在线| 国产一区二区久久久久久| 日韩午夜激情福利免费| 日韩在线免费视频精品| 久久久久久一欧美国产| 91久久亚洲成人精品| 午夜精品久久婷婷蜜桃| av熟妇翔田千里俱乐部| 亚洲五月婷婷激情图片| 激情婷婷综合久久五月天| 熟女激情一区二区三区| 国产网址手机上可以看的国产网站| 国产99精品久久久久久圆免看片| 精品国产乱码久久久久夜深| 人妻中出视频一区二区| 伊人久久中文字幕综合观看| 亚洲第一黄色天堂| 日韩av午夜免费| 色噜噜狠狠躁夜夜躁| 久久亚洲精品日韩高清| 日韩精品久久久久久久软件| 5858s在线视频| 欧美日韩精品综合在线一区二区| 婷婷亚洲天堂中文字幕| 五月天丁花香婷婷| 嫩草一区二区三区四区中文 | 日韩亚洲丝袜系列| 亚洲狠狠久久综合一区| 久久草免费在线视频| av岛国不卡在线观看| 高清一区二区三区日本4| 97久久视频免费在线播放| 欧美日韩精品在线观看免费| 男人亚洲天堂2018| 国产日韩欧美人妻在线观看| 精品人妻一区二区三区线国色天香| 国产又粗又猛又爽又黄的刘涛视| 婷婷 少妇 av| 日本高清不卡视频在线播放| 久久久久国产亚洲av麻豆| 久久精品人妻中文字幕一区| 精品激情视频在线免费观看| 色男人天堂东京热| 亚洲国产精品97久久宅男| 色yeye香蕉凹凸人妻三区| 十八禁久久久久久久久久久久久久 | 高清中文字幕乱码在线| 日韩av电影免费在线播放| 91啪啪啪内射网站| 日韩精品综合免费视频| 少妇人妻一区2区中文字幕 | 大香蕉大香蕉大香蕉大香蕉大| 秋霞在线观看色哟哟视频| 亚洲精品熟女中文字幕| 国产av在线观看麻豆| 国产免费一级a男人的天堂| 熟女大白屁股在线播放| 91插插插操美女视频| 亚洲女人中文字幕在线| 精品999久久久一免费ww| 日韩三级伦理片免费看| 视频免费在线观看91| 日日夜夜夜视频伊人久久| 五月天丁香婷婷国产| 久久亚麻亚洲蜜桃臀| jizzhd国产剧情| 欧美一区二区理论片在线观看| 青青青青青欧美在线视频观看 | 中文字幕 日本伊人| 久久久亚洲熟妇熟女在线| 日韩成人av影视在线观看| 色婷婷精品午夜在线播放| 国产成人无码AA精品一区| 婷婷 少妇 av| 日本黄页在线播放日本网站| 天天操天天干天天做天天射| 日韩男女av在线观看| 福利小视频国产一区| 91精品久久久久久久久不卡网站| 婷婷丁香花五月天| 国产五月天在线观看视频| 欧美 国产 日韩 一区二区| 国产成人精品久久久女| 欧美日本韩国乱搞视频| 久久亚洲精品日韩高清| 久久久久久精品国产成人| 亚洲aⅴ欧美综合一区二区三区| 麻豆网站在线免费看| 久久人妻少妇嫩草av蜜桃动态图 | 久久这里只有欧美精品| 精品无人区一区二区三区竹菊| 精品乱码一区二区三四五六区| 欧美精品一区二区在线视频| 国产麻豆一级美女精品| 热久久免费频精品18| 50岁老熟女一区二区三区| 蜜桃久久久一区二区三区| 欧美人妻一区二区三区在线播放 | 精品国产麻豆免费成人网| 人妻精品少妇嫩草麻豆| 亚洲女人中文字幕在线| 91国偷自产一区二区三区老熟女| 日韩欧美爱爱视频免费观看| 91一区二区三区精华液| 精品999久久久一免费ww| 欧美熟妇人妻视频在线| 成人精品一区一区二区看片| 乱丰满的岳伦在线观看| 中文字幕亚洲专区欧美| 亚洲欧美日韩中出| 亚洲不卡一区av| 97精品久久人人爽人人爽| 91制片在线观看视频| 日韩不卡一级成人免费视频| 成人黄色午夜污网站在线观看| 日韩不卡一级成人免费视频| 中文字幕理伦福利片| 色婷婷中文字幕基地| 亚洲国产一成人久久精品| 亚洲综合色激情五月| 99精品高清免费在线视频| 91精品久久久久久五月天| 国产成人精品久久久女| 亚洲 免费在线视频| av传媒高清影院免费| 亚洲少妇黄色一级片| 日韩有码在线免费观看视频| 91麻豆免费国产在线| 亚洲欧洲色图动图| 亚洲av微乳在线| 亚洲 欧美 日本 国产| 久久久久久久久久无吗| 人妻夜夜爽天天爽麻豆| 日韩手机在线一区二区三区| 亚洲av微乳在线| 婷婷亚洲免费基地| 99日本亚洲精品视频| 四十路av熟女俱乐部| 日韩一区二区三区水蜜桃 | 色哟哟视频在线观看国产| 蜜久久久91精品人妻| 日韩欧美综合一区二区在线| 97cao瑟瑟在线观看| av日韩中文字幕人妻| 日韩在线播放视频1区| 亚洲日本精彩视频在线观看| 日韩a毛片视频免费看| 日韩在线播放视频1区| 国产高潮国产高潮久久久久久91| 久久精品香蕉绿巨人| av日韩在线免费观看网站 | 国语一区二区在线观看| 伊人久久婷婷av| 91在线观看视频,| 伊人久久婷婷av| 日韩精品在线播放第三页| w疯狂的少妇2做爰韩国| 欧美日韩v中文在线| 国产精品一区二区三区三级| 中文字幕精品久久久人妻| 91啪啪啪内射网站| 91在线播放视频免费| 亚洲一区二区三区久久aa| 制服丝袜亚洲欧美第一| 天天日天天干一道一小| 国产成人精品视频免费网站| 色婷婷中文字幕基地| 久久久久中文字幕免费久久久久久| 国产精品一区二区三区福利| 91九色蝌蚪熟妇出轨| 亚洲五月婷婷视频| 97久久碰人妻一区二区三区四| 亚洲一区二区三二区厕所偷拍| 精品无人区一区二区三区竹菊 | 9久热这里有国产精品| 高潮久久久久久久av免费| 91精品久久久久久五月天| 久久久久久添逼视频| 秋霞在线观看色哟哟视频| 久久久久国产精品夜夜夜| 中文字幕成人资源网站| 在线不卡日韩视频播放| 五月爱婷婷六月丁香性| 婷婷激情五月天图片| 久久久国产一区二区视| 国产中文字幕在线91| 777亚洲精品乱码久久| 少妇激情视频一区二区| 亚洲一区视频大全| 91成人国产精品视频| 久久男人精品男人天堂免费视频| 另类h小视频在线观看| 久久精品久久一区二区三区| 91精品久久久久久久久不卡网站| 亚洲图色91东京热| 在线免费av大香蕉| 91偷伦一区二区三区蜜臀| 久久久国产精品尤物av| 成人黄片av在线播放| 草裙成人精品一区二区三区| 久久婷婷激情综合色综合俺也去| 99日本亚洲精品视频| 久久最近最新中文字幕大全| 欧美日韩v中文在线| 91在线精品一区二区网站| 国产av一区二区三区天美| 久久久久久久久久无吗| 免费av网站在线浏览| 国产一区二区三区四区中文字幕| 人妻精品无码一区二区三区| 日韩欧美亚州综合久久| 日韩av在线不卡网站| 欧美 国产 日韩 一区二区| 中文字幕人妻一区二区在线看 | 国产精品久久久久成人片| 久久久97精品国产| 亚洲男人五月天堂| 亚洲日本韩国欧美一起| 久久精品人人看人人爽| 欧美人妻a∨中文字幕在线| 久久视频精彩在线观看| 2001年亚洲区十强赛| 久久国产精品久久伊人麻豆 | 91沈先生探花极品在线| 国产精品午夜蜜av| 91麻豆精品91久久久久同性| 欧美日韩在线卡一卡二卡三| 久久精品熟女人妻一区二区三区| 亚洲天堂黄色免费| 亚洲欧洲久久精品| 久久久久国产亚洲av麻豆| 国产精品色呦呦视频免费看| 日韩av在线精品观看| 99国产在线拍91揄自揄视| 成人黄色国产网站在线观看| 天天日天天干天天色| 三级亚洲天堂亚洲天堂| 欧美熟妇精品在线观看| 高潮久久久久久久av免费| 精品97人妻无码中文永久| 久久久久久精品日韩| 久久久久久久久久久免费网| 1024欧美一区二区看片| www.成人国产精品| 亚洲 欧洲 日韩 人妻| 亚洲av微乳在线| 久久这里只有精品久| 五月天色婷婷av在线| 亚洲:西西一区二区三区四区五区| 99久久窝窝午夜影视| 国产日韩欧美人妻在线观看| 日本18禁片免费久久| 国产成人av最新网址| 日韩欧美 国产精品| 国产成人免费精品视频大全| 天美麻豆成人av精品小说| 午夜日韩麻豆福利| av激情网站在线观看| 亚洲欧美日韩综合专区| 日韩欧美 国产精品| 精品一区二区三区四区人妻69| 久久久久国产精品免费| 免费观看a级在线视频| 中文字幕一区二区三区中文字幕| 亚洲中文字幕精品高清| 国产欧美版日韩综合| 久久久久久久久久性| 日韩亚洲丝袜系列| 国产三级久久久999111| 午夜精品福利小视频| 久久爱免费视频16| 新版天堂av资源在线| 骚妻少妇一区二区| 欧美一区二区三区视频午夜| 国产一区二区视频大全床| 日本a级2020在线观看| 日韩成视频在线播放| 99免费观看视频在线| 午夜精品久久久久久久久二区三区| 99热这里只有精品网| 日韩福利视频在线看| 欧洲av偷拍亚洲av偷拍| 亚洲丰满熟妇乱xxxxx| 高清视频在线观看一区二区三区| 亚洲欧美一二三视频| 国产91久久精品一区二区老| 欧美一区二区三区激情无套| 天天日天天干天天爱天天| 狠狠干中文字幕97视频| 夜夜撸日日撸夜夜爽日日干| 日韩色在线视频观看免费| 免费观看a级在线视频| 亚洲精选黄色在线观看| 久久国产精品人妻酒店| 97久久碰人妻一区二区三区四| 亚洲情色av网站| 久久午夜福利电影免费试看| 最新偷窥偷拍免费视频观看| 亚洲激情国产一区| 久久视频黄色观看网站| 国产一级av国产免费| 久久久久久久久久无吗 | 亚洲欧洲偷拍自拍| 日韩久久久三级电影| 国产丝袜香蕉在线观看| 精品国产一区二区三区无码蜜桃| 天天做天天舔天天射| 国产成人精品久久久久久蜜臀| 精品久久久久久18免费网站| 国产欧美日韩一区二区三区''| 免费看久久久久久久性大片| 黑人操日本女人电影| 欧美va亚洲va精品| 一区二区三区四区中文字幕| 欧美人妻a∨中文字幕在线| 亚洲一级av大片| 天天操天天干天天色| 亚洲欧洲偷拍自拍| 亚洲制服欧美丝袜| 91精品人妻一区二区三区四区| 亚洲中文字幕福利视频| 中文字幕av久久爽爽| 乱丰满的岳伦在线观看| av日韩中文字幕人妻| 日韩成人av影视在线观看| 88在线观看91蜜桃国自产| 久久人搡人人玩人妻精品| 亚洲欧洲av在线| 208精品福利导航| 免费av网站在线浏览| 欧美视频 亚洲视频| 欧美日韩久久久久久精品| 久久99国产综合精品无码免费| 国产精品综合手机在线| 91麻豆精品91久久久久同性| 人妻办公室被强d在线电影| 欧美人妻a∨中文字幕在线| 91天仙tv国产福利精品| 五月天丁香婷婷国产| 亚洲伊人网在线播放| 国产成AV人片在线观看天堂无码 | huangse网站在线观看| 久久成人网男人的天堂| 玖玖玖玖日在线视频| www.色av成人| 人妻丰满一区二区三区| 欧美日韩精品亚洲欧美| 2019年中文字幕在线看| 欧美日韩精品成人在线| 亚洲 免费 在线 观看| 久久亚麻亚洲蜜桃臀| 92视频影院视频影院麻豆| 美女18禁久久久久麻豆| 欧美精品一区二区三区蜜臀| 激情五月天狠婷婷| 99久久精品国产777| 国产亚洲av免费一区二区| 久久午夜福利电影免费试看| 亚洲天堂av电影| 68国产成人综合久久精品| 99久久精品国产777| 伊人久久热青青草| 日韩卡一卡二卡三卡四卡五| 国产一区二区三区四区中文字幕 | 国产成人av吴梦梦视频| aaa久久久久久久久久网站| 大香蕉久草网一区二区三区| 国产激情综合视频在线观看| 亚洲成人av久久久久| 日韩中文字幕有码人妻在线| 日本av都有哪些系列| 国产三级久久久999111| 成人av激情网一区二区三区| 久久最近最新中文字幕大全| 欧美一二三区在线观看| 日韩厕所偷拍美女| 日韩av在线观看免费看看| 国产丝袜香蕉在线观看| 91九色蝌蚪熟妇出轨| 50岁老熟女一区二区三区| 91中日在线中文字幕| 麻豆亚州av熟女国产一区二| 国产网址手机上可以看的国产网站| 久久久精品欧美一区二区国产| 四虎网站免费av| 日韩av中文字幕一区| 五月婷久久综合狠狠爱97| 日韩av中文网址| 亚洲欧美日韩中出| 久久伊人精品青青草原| 久久久久久少妇被弄高潮| 中文字幕一区二区三区中文字幕| 欧美区 日韩区 国产区| 国产亚洲av免费一区二区| 国产99成人自拍视频| 欧美熟女高清视频一区二区| 日韩av中文字幕一区| 人妻丰满精品一区二区三区| 午夜在线观看高清完整版| 亚洲精品一区二区三区丝袜| 中文字幕的不卡人妻| 日韩欧美国产成人一区| 色哟哟免费在线播放| 亚洲av不卡码在线看| 欧美日本韩国乱搞视频| 日韩中文字幕在线综合网| 91九色porny国产视频| 欧美老熟妇重口另类xxx| 中文字幕国产在线观看| 88在线观看91蜜桃国自产| 国产网址手机上可以看的国产网站| 五月婷婷激情狠狠| 中文字幕一区二区不卡顿| 嫩草一区二区三区四区中文| 中文字幕日韩天堂| 久久av一区中文字幕| 国产日韩亚洲大尺度高清| 亚洲天堂黄色在线| 久久99精品久久久久久三级| 日韩av在线精品观看| 免费麻豆国产一区二区三区| 欧美一级黄片视频看看| 亚洲中文字幕精品高清| 亚洲欧洲av在线| 中文字幕一区二区三区中文字幕| 亚洲乳大丰满中文字幕少妇av| 亚洲欧美日韩偷拍综合| 国产黄色主播网址大全在线播放| 久久视频一区二区三| 国产欧美日韩亚洲另类第一第二页 | 久久久久久久久久久最新| 日韩av一区中文| 亚洲av不卡码在线看| 久久久久国产精品夜夜夜| 久久亚洲熟妇中文字幕| 亚洲精品久久第一页| av在线免费观看网站大全| 狠狠干中文字幕97视频| 久久亚洲精品无码系列客服 | 国语一区二区在线观看| 中文字幕人妻一区二区在线看| 99久久999久久久精品综合| 337p亚洲精品色噜噜| 日韩av一区中文| a级黄片在线免费观看| 五月婷婷激情狠狠| 天天日天天干一道一小| 草裙成人精品一区二区三区| 日本加勒比中文字幕久久| 国产精品久久久久久吹吹潮| 久久人人爽爽人人爽av| 免费中文字幕在线播放| 日韩美女影院免费在线观看| 日韩有码视频在线免费观看| 97cao瑟瑟在线观看| 亚洲熟妇在线观看一区二区| 久久最近最新中文字幕大全| 亚洲欧洲久久精品| 日韩精品免费91aa| 欧美肥臀精品一区二区三区| 亚洲综合精品久久| 九九久久国产精品久久久久| 一本大道av伊人久久综合| 激情婷婷综合久久五月天| 国产91色在线i免费| 国产精品首页在线播放| 日韩a大片在线观看| 污视频在线观看91| 欧美一区二区精品人妻免费视频| 欧美熟女人妻一区二区三区 | 国产91色在线i免费| 亚洲av微乳在线| 国产日韩一区二区三区啪啪啪| 色丁香婷婷综合久久| 91一区二区三区精华液| 人人妻人人爱碰千| w疯狂的少妇2做爰韩国| 久久久久在线免费看| 欧美老熟妇重口另类xxx| 欧美高清在线视频99| 亚洲av综合色区手机| 色视频精品视频在线观看| 日韩福利视频在线看| 亚洲精品蜜桃久久久久| 日韩尤物人妻av在线网| 国产自拍 偷拍 在线| 最新偷窥偷拍免费视频观看| 久久视频精彩在线观看| japanese 在线中文字幕| 久久免费视频观看99| 丰满少妇精品一区二区| 国产人妻另类综合专区| 久久精品国产亚洲av久丨| 国产99不卡免费在线观看| 久久99精品国产91| 熟妇人妻 一区二区三区中文字幕| huangse网站在线观看| 人人妻人人澡人人爽dv| 亚洲中文字幕福利视频| 精品久久久久久999| 久久精品人人看人人爽| 久久久久久添逼视频| 国产日韩一区二区三区啪啪啪| 亚洲狠狠久久综合一区| 篠田优中文字幕在线播| 国产a级片免费在线观看| 久久久国产一区二区视| 亚洲综合成人久久av| 欧美一区二区三区综合色| 又粗又猛又爽黄老大爷视频| 天天日天天干天天爱天天| 麻豆精品一区在线免费观看| 亚洲av电影在线一区二区| 精品人妻久久久久中文字幕19禁| 婷婷在线综合观看视频| 中文字幕亚洲中文字幕| 97国产人妻换人妻| 久久视频这里都是精品| 99久久久99久久91熟女| 日本久久久大片中文字幕| 美女在线观看亚洲一区| 亚洲最黄色的视频| 欧美性久久久久久久久久久| 欧美日韩激情在线视频观看| 天天色天天干天天舔| 91九色porny国产视频| 亚洲 免费在线视频| 人妻精品少妇嫩草麻豆| 天天色天天日天天操| 人妻一区二区中文字幕在线| 97成人免费碰碰碰视频| 亚洲欧美日韩国产另类专区| 欧洲美女黑人粗性暴交视| 日韩av午夜免费| 成人人妻视频一区二区| 91亚洲精品成人在线| av色香蕉一区二区三区| 国产精品一二三四在线| 超碰97在线观看国产| 日韩大全毛片免费观看视频| 国产精品久久久久久久久久网站 | 精品人妻久久久久中文字幕19禁 | 色综合色综合色综合久久| 欧美人妻系列,中文字幕| 亚洲香蕉av电影| 青青草青青操在线播放| 亚洲一线产区二线产区区| 9久热这里有国产精品| 久久在线人妻熟女高清完整版| 99久久精品国产777| 久久国产精品——国产精品| www日本不卡一二三区| 国产成人精品视频免费网站| 国产欧美一区二区二区精品|